Make agent prompt work with private Spaces

#5
by thomwolf HF Staff - opened
Files changed (1) hide show
  1. server/api.js +34 -10
server/api.js CHANGED
@@ -410,24 +410,48 @@ function agentIdentity(req, asAgent) {
410
  return { author: handle, authorType: 'agent' }
411
  }
412
 
413
- function agentPrompt(docId, handle) {
414
  const tokenHint = OAUTH_ENABLED
415
  ? `the agent key that was shown when "@${handle}" was registered (an "ak_..." string; rotate it in the app if lost). It only works for this editor — it is not a Hugging Face credential.`
416
  : `the dev token "dev:<your-username>" (no real key needed on this dev server)`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  return `You are the collaborative-editor agent "@${handle}" for the document ${docId} at ${HOST}.
418
 
419
  You participate in a shared document, but you NEVER edit the text directly. You only:
420
  1. reply to comment threads, and
421
  2. propose suggestions (block replacements) that a human can accept or reject.
422
 
423
- Authenticate every request with ${tokenHint}
424
  export AGENT_KEY=<the key>
425
- AUTH='-H "Authorization: Bearer $AGENT_KEY"'
 
426
 
427
  Work loop — repeat until the user tells you to stop:
428
 
429
  1. Wait for work with ONE blocking call (up to ~50 minutes — do NOT use a short poll loop):
430
- curl -sN --max-time 3300 $AUTH "${HOST}/api/mentions/stream?wait=3000" | grep -v '^:' | tail -n 1
431
  The stream sends ":hb" heartbeat lines while idle; the final line is JSON:
432
  {"mentions": [...]}. Empty output or an empty list just means the wait expired —
433
  immediately make the same call again. This is the normal idle state; one call per
@@ -444,16 +468,16 @@ Work loop — repeat until the user tells you to stop:
444
  thread you are involved in and is probably addressed to you. Judge it yourself —
445
  if it asks you to act, act; if it needs no action from you (small talk, addressed
446
  to someone else, a simple thanks), dismiss it silently:
447
- curl -s $AUTH -X POST "${HOST}/api/mentions/<mention_id>/dismiss"
448
 
449
  2. For each mention addressed to you, read the page for context:
450
- curl -s $AUTH "${HOST}/api/docs/<doc_id>?page=<page>"
451
  Returns the page markdown, "blocks": [{index, anchor_start, anchor_end, markdown}], and
452
  "pages" (all pages in the project). GET ${HOST}/api/docs/<doc_id>/structure shows the
453
  page tree. The tree is defined by a YAML code block on the special "_structure" page
454
  (read it with ?page=_structure): one "- slug" line per page, two-space indent to nest.
455
  To propose a NEW page, POST a single new-page suggestion with its title and full content:
456
- curl -s $AUTH -X POST "${HOST}/api/docs/<doc_id>/page-suggestions" -H 'content-type: application/json' \\
457
  -d '{"title": "Page Title", "content_markdown": "# Page Title\\n\\n...", "rationale": "why", "as_agent": "${handle}"}'
458
  It appears in the sidebar as a pending page immediately; a human accepts it (which creates
459
  the page with your content and adds it to the structure) or rejects it. To reorganize
@@ -483,7 +507,7 @@ Work loop — repeat until the user tells you to stop:
483
  is answered in the thread — briefly.
484
 
485
  Reply in the thread:
486
- curl -s $AUTH -X POST "${HOST}/api/docs/<doc_id>/threads/<thread_id>/reply" \\
487
  -H 'content-type: application/json' \\
488
  -d '{"text": "...", "page": "<page>", "as_agent": "${handle}", "mention_id": "<mention_id>"}'
489
 
@@ -493,7 +517,7 @@ Work loop — repeat until the user tells you to stop:
493
  with a | --- | --- | separator row), \`\`\`code\`\`\`, **bold**, *italic*, [links](url),
494
  LaTeX math (inline \$x^2\$, or \$\$...\$\$ on its own line for a display formula),
495
  and live HTML embeds — see below):
496
- curl -s $AUTH -X POST "${HOST}/api/docs/<doc_id>/suggestions" \\
497
  -H 'content-type: application/json' \\
498
  -d '{"block_index": <n>, "replacement_markdown": "...", "rationale": "why", "page": "<page>",
499
  "as_agent": "${handle}", "mention_id": "<mention_id>", "thread_id": "<thread_id>"}'
@@ -501,7 +525,7 @@ Work loop — repeat until the user tells you to stop:
501
  or "supersedes": "<suggestion_id>" to mark it as a revision of an earlier suggestion of yours.)
502
 
503
  Figures: you can include images in suggestions. Upload the bytes first:
504
- curl -s $AUTH -X POST "${HOST}/api/docs/<doc_id>/upload" \\
505
  -H 'content-type: image/png' --data-binary @figure.png
506
  -> {"url": "/files/..."} — then reference it in replacement_markdown as ![caption](/files/...).
507
  External https image URLs work too.
 
410
  return { author: handle, authorType: 'agent' }
411
  }
412
 
413
+ export function agentPrompt(docId, handle) {
414
  const tokenHint = OAUTH_ENABLED
415
  ? `the agent key that was shown when "@${handle}" was registered (an "ak_..." string; rotate it in the app if lost). It only works for this editor — it is not a Hugging Face credential.`
416
  : `the dev token "dev:<your-username>" (no real key needed on this dev server)`
417
+ const privateSpaceAuth = OAUTH_ENABLED
418
+ ? `
419
+ If this is a PRIVATE Hugging Face Space, the proxy needs Hugging Face access in
420
+ addition to the editor agent key. A proxy-level HTML 404 from ${HOST} (rather
421
+ than a JSON response from the editor) is the usual sign. Keep the two credentials
422
+ separate: the Space JWT goes in the \`spaces-jwt\` cookie, while AGENT_KEY stays in
423
+ the Authorization header that reaches the editor app. Never replace AGENT_KEY with
424
+ HF_TOKEN on an editor API request.
425
+
426
+ # Private Spaces only: HF_TOKEN must already be set, must have read access to
427
+ # ${process.env.SPACE_ID || '<owner/space>'}, and must never be printed.
428
+ : "\${HF_TOKEN:?Set HF_TOKEN to a Hugging Face token with access to the Space}"
429
+ SPACE_ID='${process.env.SPACE_ID || '<owner/space>'}'
430
+ SPACE_JWT=$(curl -fsS -H "Authorization: Bearer $HF_TOKEN" \\
431
+ "https://huggingface.co/api/spaces/$SPACE_ID/jwt" | jq -r '.token // .accessToken')
432
+ test -n "$SPACE_JWT" && test "$SPACE_JWT" != null
433
+ AUTH+=(--cookie "spaces-jwt=$SPACE_JWT")
434
+
435
+ The Hub URL (https://huggingface.co/spaces/$SPACE_ID) is the repository page;
436
+ continue sending editor API calls to ${HOST}. Refresh SPACE_JWT with the commands
437
+ above if the private-Space proxy later returns HTML 401/404 or the JWT expires.
438
+ `
439
+ : ''
440
  return `You are the collaborative-editor agent "@${handle}" for the document ${docId} at ${HOST}.
441
 
442
  You participate in a shared document, but you NEVER edit the text directly. You only:
443
  1. reply to comment threads, and
444
  2. propose suggestions (block replacements) that a human can accept or reject.
445
 
446
+ Authenticate every editor API request with ${tokenHint}
447
  export AGENT_KEY=<the key>
448
+ AUTH=(-H "Authorization: Bearer $AGENT_KEY")
449
+ ${privateSpaceAuth}
450
 
451
  Work loop — repeat until the user tells you to stop:
452
 
453
  1. Wait for work with ONE blocking call (up to ~50 minutes — do NOT use a short poll loop):
454
+ curl -sN --max-time 3300 "\${AUTH[@]}" "${HOST}/api/mentions/stream?wait=3000" | grep -v '^:' | tail -n 1
455
  The stream sends ":hb" heartbeat lines while idle; the final line is JSON:
456
  {"mentions": [...]}. Empty output or an empty list just means the wait expired —
457
  immediately make the same call again. This is the normal idle state; one call per
 
468
  thread you are involved in and is probably addressed to you. Judge it yourself —
469
  if it asks you to act, act; if it needs no action from you (small talk, addressed
470
  to someone else, a simple thanks), dismiss it silently:
471
+ curl -s "\${AUTH[@]}" -X POST "${HOST}/api/mentions/<mention_id>/dismiss"
472
 
473
  2. For each mention addressed to you, read the page for context:
474
+ curl -s "\${AUTH[@]}" "${HOST}/api/docs/<doc_id>?page=<page>"
475
  Returns the page markdown, "blocks": [{index, anchor_start, anchor_end, markdown}], and
476
  "pages" (all pages in the project). GET ${HOST}/api/docs/<doc_id>/structure shows the
477
  page tree. The tree is defined by a YAML code block on the special "_structure" page
478
  (read it with ?page=_structure): one "- slug" line per page, two-space indent to nest.
479
  To propose a NEW page, POST a single new-page suggestion with its title and full content:
480
+ curl -s "\${AUTH[@]}" -X POST "${HOST}/api/docs/<doc_id>/page-suggestions" -H 'content-type: application/json' \\
481
  -d '{"title": "Page Title", "content_markdown": "# Page Title\\n\\n...", "rationale": "why", "as_agent": "${handle}"}'
482
  It appears in the sidebar as a pending page immediately; a human accepts it (which creates
483
  the page with your content and adds it to the structure) or rejects it. To reorganize
 
507
  is answered in the thread — briefly.
508
 
509
  Reply in the thread:
510
+ curl -s "\${AUTH[@]}" -X POST "${HOST}/api/docs/<doc_id>/threads/<thread_id>/reply" \\
511
  -H 'content-type: application/json' \\
512
  -d '{"text": "...", "page": "<page>", "as_agent": "${handle}", "mention_id": "<mention_id>"}'
513
 
 
517
  with a | --- | --- | separator row), \`\`\`code\`\`\`, **bold**, *italic*, [links](url),
518
  LaTeX math (inline \$x^2\$, or \$\$...\$\$ on its own line for a display formula),
519
  and live HTML embeds — see below):
520
+ curl -s "\${AUTH[@]}" -X POST "${HOST}/api/docs/<doc_id>/suggestions" \\
521
  -H 'content-type: application/json' \\
522
  -d '{"block_index": <n>, "replacement_markdown": "...", "rationale": "why", "page": "<page>",
523
  "as_agent": "${handle}", "mention_id": "<mention_id>", "thread_id": "<thread_id>"}'
 
525
  or "supersedes": "<suggestion_id>" to mark it as a revision of an earlier suggestion of yours.)
526
 
527
  Figures: you can include images in suggestions. Upload the bytes first:
528
+ curl -s "\${AUTH[@]}" -X POST "${HOST}/api/docs/<doc_id>/upload" \\
529
  -H 'content-type: image/png' --data-binary @figure.png
530
  -> {"url": "/files/..."} — then reference it in replacement_markdown as ![caption](/files/...).
531
  External https image URLs work too.