@agentconnect.md/daemon 1.0.0-rc.297 → 1.0.0-rc.299

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/{acp-host-03mQVw2Q.js → acp-host-DezWSmEG.js} +2 -2
  2. package/dist/{acp-host-03mQVw2Q.js.map → acp-host-DezWSmEG.js.map} +1 -1
  3. package/dist/{agent-list-DHFLd9ux.js → agent-list-Da9Osn4O.js} +2 -2
  4. package/dist/{agent-list-DHFLd9ux.js.map → agent-list-Da9Osn4O.js.map} +1 -1
  5. package/dist/{chat-IRJd4_lj.js → chat-D8QIIHor.js} +3 -3
  6. package/dist/{chat-IRJd4_lj.js.map → chat-D8QIIHor.js.map} +1 -1
  7. package/dist/{load-agents-8AzZXdVx.js → dist-Cq1Kimg_.js} +18 -399
  8. package/dist/dist-Cq1Kimg_.js.map +1 -0
  9. package/dist/gh-token-BfPlYQav.js +97 -0
  10. package/dist/gh-token-BfPlYQav.js.map +1 -0
  11. package/dist/{git-credential-DLRF9bOx.js → git-credential-DhWn1IBJ.js} +26 -8
  12. package/dist/git-credential-DhWn1IBJ.js.map +1 -0
  13. package/dist/{gitcred-server-Dhp-tyEI.js → gitcred-server-KXxLyYxc.js} +107 -55
  14. package/dist/gitcred-server-KXxLyYxc.js.map +1 -0
  15. package/dist/index.js +9 -5
  16. package/dist/index.js.map +1 -1
  17. package/dist/load-agents-gTqVkOwl.js +401 -0
  18. package/dist/load-agents-gTqVkOwl.js.map +1 -0
  19. package/dist/{login-xtaBgstv.js → login-BP4bFP-d.js} +4 -3
  20. package/dist/{login-xtaBgstv.js.map → login-BP4bFP-d.js.map} +1 -1
  21. package/dist/{run-foreground-BHU49ZSi.js → run-foreground-B8sw7WKS.js} +216 -143
  22. package/dist/{run-foreground-BHU49ZSi.js.map → run-foreground-B8sw7WKS.js.map} +1 -1
  23. package/dist/run-foreground-jOExu3YR.js +2 -0
  24. package/package.json +1 -1
  25. package/dist/git-credential-DLRF9bOx.js.map +0 -1
  26. package/dist/gitcred-server-Dhp-tyEI.js.map +0 -1
  27. package/dist/load-agents-8AzZXdVx.js.map +0 -1
  28. package/dist/run-foreground-BiYUl931.js +0 -2
@@ -3,11 +3,13 @@ import { n as require_ms, t as require_src$3 } from "./src-DDtlVutj.js";
3
3
  import { n as init_esm_shims, t as __dirname } from "./esm-shims-DxGcm4Hw.js";
4
4
  import { t as DAEMON_VERSION } from "./version-DGaRXjBK.js";
5
5
  import { C as _coercedNumber, T as NEVER, a as array, d as literal, f as looseObject, i as any, m as object, o as boolean, p as number$1, t as ZodNumber, v as string, x as url$4 } from "./schemas-CQkO9hC5.js";
6
- import { C as ZodIssueCode, S as RESERVED_MCP_SERVER_NAME, _ as buildEnvelope, a as agentChildEnv, b as MAX_FRAME_BYTES, c as persistRelays, d as normalizeGitUrl, f as RELAY_DAEMON_SUBPROTOCOL, g as SHARED_CONFIG_ACTION_ID, h as decodeRelayDaemonFrame, i as selectAgent, m as buildRelayDaemonFrame, n as findAgentFiles, o as loadConfig, p as RELAY_DAEMON_WS_PATH, r as loadAgents, s as persistDaemonId, v as decodeEnvelope, y as encode } from "./load-agents-8AzZXdVx.js";
6
+ import { a as RELAY_DAEMON_WS_PATH, c as SHARED_CONFIG_ACTION_ID, d as encode, f as MAX_FRAME_BYTES, g as ZodIssueCode, h as RESERVED_MCP_SERVER_NAME, i as RELAY_DAEMON_SUBPROTOCOL, l as buildEnvelope, n as gitRepoLabel, o as buildRelayDaemonFrame, r as normalizeGitUrl, s as decodeRelayDaemonFrame, u as decodeEnvelope } from "./dist-Cq1Kimg_.js";
7
+ import { a as agentChildEnv, c as persistRelays, i as selectAgent, n as findAgentFiles, o as loadConfig, r as loadAgents, s as persistDaemonId } from "./load-agents-gTqVkOwl.js";
7
8
  import { l as resolveRoot, o as mcpSocketPath, u as statePath } from "./paths-DEcUO8lC.js";
8
- import { S as resolveRuntimes, _ as writeMemoryFile, a as initGitInjection, b as cachedRuntimeNames, c as simpleGit, d as memoryProviderFor, f as MemoryConflictError, g as resolveInMemoryDir, h as listMemory, i as prepareWorkspace, l as createMemoryProvider, m as MemoryTooLargeError, n as installedRuntimes, o as probeGitVersion, p as MemoryPathError, r as prefetchWorkspace, s as sessionGitEnv, t as AcpHost, u as memoryKindOf, v as MEMORY_TOOL_NAMES, x as cachedRuntimeVersions, y as toolsForIntegrations } from "./acp-host-03mQVw2Q.js";
9
- import { i as GitCredentialCache, n as gitcredSocketPath, r as writeGitcredShim, t as GitCredServer } from "./gitcred-server-Dhp-tyEI.js";
9
+ import { S as resolveRuntimes, _ as writeMemoryFile, a as initGitInjection, b as cachedRuntimeNames, c as simpleGit, d as memoryProviderFor, f as MemoryConflictError, g as resolveInMemoryDir, h as listMemory, i as prepareWorkspace, l as createMemoryProvider, m as MemoryTooLargeError, n as installedRuntimes, o as probeGitVersion, p as MemoryPathError, r as prefetchWorkspace, s as sessionGitEnv, t as AcpHost, u as memoryKindOf, v as MEMORY_TOOL_NAMES, x as cachedRuntimeVersions, y as toolsForIntegrations } from "./acp-host-DezWSmEG.js";
10
+ import { i as GitCredentialCache, n as gitcredSocketPath, r as writeGitcredShim, t as GitCredServer } from "./gitcred-server-KXxLyYxc.js";
10
11
  import { B as ToolListChangedNotificationSchema, D as ListPromptsResultSchema, E as ListChangedOptionsBaseSchema, F as PromptListChangedNotificationSchema, G as isZ4Schema, H as isJSONRPCRequest, I as ReadResourceResultSchema, J as encodeFrame, K as safeParse, L as ResourceListChangedNotificationSchema, M as ListToolsResultSchema, O as ListResourceTemplatesResultSchema, P as McpError, R as SUPPORTED_PROTOCOL_VERSIONS, S as InitializeResultSchema, T as LATEST_PROTOCOL_VERSION, U as isJSONRPCResultResponse, V as isInitializedNotification, W as getObjectShape, _ as ElicitResultSchema, a as AjvJsonSchemaValidator, b as GetPromptResultSchema, c as mergeCapabilities, d as CompleteResultSchema, f as CreateMessageRequestSchema, g as ElicitRequestSchema, h as CreateTaskResultSchema, i as assertToolsCallTaskCapability, k as ListResourcesResultSchema, m as CreateMessageResultWithToolsSchema, n as serializeMessage, o as require_fast_deep_equal, p as CreateMessageResultSchema, q as decodeFrames, r as assertClientRequestTaskCapability, s as Protocol, t as ReadBuffer, u as CallToolResultSchema, v as EmptyResultSchema, w as JSONRPCMessageSchema, y as ErrorCode } from "./stdio-DnS8WcXi.js";
12
+ import { createRequire } from "node:module";
11
13
  import { URL as URL$1 } from "url";
12
14
  import * as tls from "tls";
13
15
  import * as net$5 from "net";
@@ -7583,6 +7585,119 @@ var McpControlServer = class {
7583
7585
  }
7584
7586
  };
7585
7587
  //#endregion
7588
+ //#region src/cp/gh-shim.ts
7589
+ /**
7590
+ * run/bin/gh — the gh wrapper shim (agent-multi-repo-authorization.md decision
7591
+ * 4, issue #457).
7592
+ *
7593
+ * `gh` has no credential-helper hook: it reads a STATIC `GH_TOKEN` fixed at
7594
+ * process spawn, which is why P2.5's injected token went stale after ≤1h and
7595
+ * could only ever name the workspace repo. The daemon prepends `run/bin` to
7596
+ * the agent runtime's PATH so every `gh …` the agent runs lands here first;
7597
+ * the wrapper resolves the TARGET repo (GH_REPO → -R/--repo → cwd origin),
7598
+ * pulls that repo's fresh token through gitcred.sock (`agentconnect gh-token`,
7599
+ * same socket + cache as the git credential helper) and exec's the real gh
7600
+ * with GH_TOKEN set. Per-invocation fresh + per-repo correct.
7601
+ *
7602
+ * Precedence: a USER-supplied GH_TOKEN must win (the `daemon.ts` env-injection
7603
+ * contract) — the daemon exports the spawn token under both GH_TOKEN and the
7604
+ * AC_GH_TOKEN_INJECTED sentinel, so `GH_TOKEN != sentinel` means the user set
7605
+ * their own and the wrapper defers entirely. When the daemon can't serve a
7606
+ * token (repo unauthorized, CP down) the wrapper prints the daemon's
7607
+ * actionable reason on stderr and falls back to the spawn-time env token.
7608
+ *
7609
+ * The script is SECRET-FREE (paths + agent id only) and regenerated every
7610
+ * daemon boot, like `git-credential-helper.sh`. Tokens transit a shell
7611
+ * variable and the exec'd process env — never argv, never disk.
7612
+ */
7613
+ function ghShimDir(root) {
7614
+ return join(root, "run", "bin");
7615
+ }
7616
+ /** (Re)write `run/bin/gh` and return the bin dir to prepend to agent PATHs. */
7617
+ function writeGhShim(root, cliEntry) {
7618
+ const dir = ghShimDir(root);
7619
+ mkdirSync(dir, {
7620
+ recursive: true,
7621
+ mode: 448
7622
+ });
7623
+ try {
7624
+ chmodSync(dir, 448);
7625
+ } catch {}
7626
+ const q = (v) => `'${v.replaceAll("'", "'\\''")}'`;
7627
+ const argv = [q(process.execPath)];
7628
+ if (cliEntry.endsWith(".ts")) {
7629
+ const req = createRequire(import.meta.url);
7630
+ argv.push(q(req.resolve("tsx/cli")));
7631
+ }
7632
+ argv.push(q(cliEntry));
7633
+ const cli = argv.join(" ");
7634
+ const body = `#!/bin/sh
7635
+ # agentconnect gh wrapper — regenerated on daemon start; NO secrets.
7636
+ # Per-repo, per-invocation GH_TOKEN via the daemon socket (issue #457).
7637
+
7638
+ SELF_DIR=${q(dir)}
7639
+
7640
+ # The real gh: first one on PATH outside our own bin dir.
7641
+ REAL_GH=""
7642
+ _OLDIFS=$IFS; IFS=:
7643
+ for _d in $PATH; do
7644
+ [ "$_d" = "$SELF_DIR" ] && continue
7645
+ if [ -x "$_d/gh" ]; then REAL_GH="$_d/gh"; break; fi
7646
+ done
7647
+ IFS=$_OLDIFS
7648
+ if [ -z "$REAL_GH" ]; then
7649
+ echo "agentconnect: gh is not installed on this machine" >&2
7650
+ exit 127
7651
+ fi
7652
+
7653
+ # A user-configured GH_TOKEN wins: the daemon-injected one always equals the
7654
+ # AC_GH_TOKEN_INJECTED sentinel, so any difference means "the user set it".
7655
+ if [ -n "$GH_TOKEN" ] && [ "$GH_TOKEN" != "$AC_GH_TOKEN_INJECTED" ]; then
7656
+ exec "$REAL_GH" "$@"
7657
+ fi
7658
+ if [ -z "$AC_AGENT_ID" ]; then
7659
+ exec "$REAL_GH" "$@"
7660
+ fi
7661
+
7662
+ # Target repo: -R/--repo argv (all spellings, last wins — matches gh's pflag),
7663
+ # then GH_REPO env (gh gives the flag precedence over the env), then the cwd
7664
+ # origin remote. Scans a COPY of the args (for-loop doesn't consume "$@").
7665
+ _REPO=""
7666
+ _prev=""
7667
+ for _a in "$@"; do
7668
+ case "$_prev" in
7669
+ -R|--repo) _REPO="$_a" ;;
7670
+ esac
7671
+ case "$_a" in
7672
+ --repo=*) _REPO="\${_a#--repo=}" ;;
7673
+ -R=*) _REPO="\${_a#-R=}" ;;
7674
+ -R?*) _REPO="\${_a#-R}" ;;
7675
+ esac
7676
+ _prev="$_a"
7677
+ done
7678
+ if [ -z "$_REPO" ]; then _REPO="$GH_REPO"; fi
7679
+ if [ -z "$_REPO" ]; then
7680
+ _REPO=$(git remote get-url origin 2>/dev/null || true)
7681
+ fi
7682
+
7683
+ # Fresh token from the daemon (stdout = token only; stderr passes through so
7684
+ # the agent can read WHY a repo was refused). Exit 2 = "not ours" (non-github
7685
+ # host) — run the real gh untouched.
7686
+ _TOKEN=$(AGENTCONNECT_ROOT=${q(root)} ${cli} gh-token "$AC_AGENT_ID" "$_REPO")
7687
+ _RC=$?
7688
+ if [ "$_RC" -eq 2 ]; then
7689
+ exec "$REAL_GH" "$@"
7690
+ fi
7691
+ if [ -n "$_TOKEN" ]; then
7692
+ GH_TOKEN="$_TOKEN" exec "$REAL_GH" "$@"
7693
+ fi
7694
+ # Daemon couldn't serve one — fall back to the spawn-time env token (if any).
7695
+ exec "$REAL_GH" "$@"
7696
+ `;
7697
+ writeFileSync(join(dir, "gh"), body, { mode: 493 });
7698
+ return dir;
7699
+ }
7700
+ //#endregion
7586
7701
  //#region src/mcp/inject.ts
7587
7702
  /**
7588
7703
  * Build the `mcpServers` entry that makes the agent harness spawn our stdio
@@ -197330,12 +197445,9 @@ function buildHookMessage(msg, traceId) {
197330
197445
  }
197331
197446
  //#endregion
197332
197447
  //#region src/github/poster.ts
197333
- const DEFAULT_EDIT_INTERVAL_MS = 2e3;
197334
197448
  const DEFAULT_FINALIZE_TIMEOUT_MS = 15e3;
197335
197449
  /** GitHub caps comment bodies at 65536 chars — truncate with a marker well below. */
197336
197450
  const MAX_COMMENT_CHARS = 6e4;
197337
- /** Consecutive API failures before the stream gives up for this turn. */
197338
- const MAX_FAILURES = 3;
197339
197451
  const TRUNCATION_MARKER = "\n\n_(truncated — see the session transcript for the full reply)_";
197340
197452
  const LEGACY_BOUNDARIES = /* @__PURE__ */ new Set([
197341
197453
  "agent_thought_chunk",
@@ -197362,17 +197474,17 @@ var GithubReplyCollector = class {
197362
197474
  lastCommentarySeen = -1;
197363
197475
  legacyRuns = [];
197364
197476
  legacyCurrent;
197365
- /** Consume one raw ACP update. Returns the complete in-progress final answer, if authoritative. */
197477
+ /** Consume one raw ACP update; the completed answer is selected by finalText(). */
197366
197478
  onUpdate(update) {
197367
197479
  const u = record(update);
197368
- if (!u) return void 0;
197480
+ if (!u) return;
197369
197481
  const kind = typeof u.sessionUpdate === "string" ? u.sessionUpdate : void 0;
197370
197482
  if (kind !== "agent_message_chunk") {
197371
197483
  if (kind && LEGACY_BOUNDARIES.has(kind)) this.commitLegacyBoundary();
197372
197484
  return;
197373
197485
  }
197374
197486
  const content = record(u.content);
197375
- if (content?.type !== "text" || typeof content.text !== "string") return void 0;
197487
+ if (content?.type !== "text" || typeof content.text !== "string") return;
197376
197488
  const text = content.text;
197377
197489
  const seen = this.nextOrder++;
197378
197490
  const codex = record(record(u._meta)?.codex);
@@ -197416,7 +197528,6 @@ var GithubReplyCollector = class {
197416
197528
  this.legacyCurrent.text += text;
197417
197529
  this.legacyCurrent.lastSeen = seen;
197418
197530
  }
197419
- return this.explicitFinalText();
197420
197531
  }
197421
197532
  finalText(completed = true) {
197422
197533
  const explicit = this.explicitFinalText();
@@ -197519,27 +197630,14 @@ function truncatedMarkdownPrefix(text, budget) {
197519
197630
  contentBudget = nextBudget;
197520
197631
  }
197521
197632
  }
197522
- var GithubCommentStream = class {
197633
+ var GithubFinalPoster = class {
197523
197634
  deps;
197524
197635
  repo;
197525
197636
  issueNumber;
197526
- buffer = "";
197527
- /** Rendered body actually on GitHub. Comparing rendered output avoids no-op
197528
- * PATCHes once an overlong reply has already filled the truncation budget. */
197529
- postedBody = null;
197530
- commentId = null;
197531
- inFlight = Promise.resolve();
197532
- busy = false;
197533
- timer = null;
197534
- hasSent = false;
197535
- lastSendAt = 0;
197536
- failures = 0;
197537
- finished = false;
197538
197637
  abandoned = false;
197539
- finishPromise;
197638
+ publishPromise;
197540
197639
  abort = new AbortController();
197541
197640
  sched;
197542
- editIntervalMs;
197543
197641
  finalizeTimeoutMs;
197544
197642
  footer;
197545
197643
  constructor(deps, repo, issueNumber, attribution) {
@@ -197551,110 +197649,67 @@ var GithubCommentStream = class {
197551
197649
  setTimeout: (fn, ms) => setTimeout(fn, ms),
197552
197650
  clearTimeout: (h) => clearTimeout(h)
197553
197651
  };
197554
- this.editIntervalMs = deps.minEditIntervalMs ?? DEFAULT_EDIT_INTERVAL_MS;
197555
197652
  this.finalizeTimeoutMs = deps.finalizeTimeoutMs ?? DEFAULT_FINALIZE_TIMEOUT_MS;
197556
197653
  this.footer = attributionFooter(attribution);
197557
197654
  }
197558
- /** Fold one agent-output chunk in; schedules the create/edit as needed. Never throws. */
197559
- append(text) {
197560
- if (this.finished || this.abandoned || this.failures >= MAX_FAILURES || !text) return;
197561
- this.buffer += text;
197562
- this.schedule();
197563
- }
197564
- /** Replace the streamed final-answer body with its complete value (messageId-safe). */
197565
- replace(text) {
197566
- if (this.finished || this.abandoned || this.failures >= MAX_FAILURES || !text) return;
197567
- this.buffer = text;
197568
- this.schedule();
197569
- }
197570
197655
  /**
197571
- * Turn over: flush whatever is left as the final body. Resolves once the
197572
- * last write settled (or was abandoned); never rejects. Idempotent.
197656
+ * Publish the completed turn's final body exactly once. Resolves once the POST
197657
+ * settles (or the bounded wait expires); never rejects. Idempotent: the first
197658
+ * call wins, including an empty final.
197573
197659
  */
197574
- finish(finalBody) {
197575
- if (!this.finishPromise) this.finishPromise = this.finishOnce(finalBody);
197576
- return this.finishPromise;
197660
+ publish(finalBody) {
197661
+ if (!this.publishPromise) this.publishPromise = this.publishOnce(finalBody);
197662
+ return this.publishPromise;
197577
197663
  }
197578
- async finishOnce(finalBody) {
197579
- if (finalBody !== void 0) this.buffer = finalBody;
197580
- this.finished = true;
197581
- if (this.timer !== null) {
197582
- this.sched.clearTimeout(this.timer);
197583
- this.timer = null;
197584
- }
197585
- if (!this.buffer.trim() && !this.busy) return;
197664
+ async publishOnce(finalBody) {
197665
+ if (!finalBody?.trim()) return;
197586
197666
  let deadlineHandle;
197587
197667
  try {
197668
+ const deadlineAt = this.sched.now() + this.finalizeTimeoutMs;
197588
197669
  const deadline = new Promise((resolve) => {
197589
- deadlineHandle = this.sched.setTimeout(() => {
197590
- this.abandoned = true;
197591
- this.abort.abort();
197592
- try {
197593
- this.deps.log.warn(`github poster: final flush timed out on ${this.repo}#${this.issueNumber}`);
197594
- } catch {} finally {
197670
+ try {
197671
+ deadlineHandle = this.sched.setTimeout(() => {
197672
+ this.abandonTimedOut();
197595
197673
  resolve();
197596
- }
197597
- }, this.finalizeTimeoutMs);
197598
- });
197599
- const drain = (async () => {
197600
- await this.inFlight;
197601
- if (this.abandoned) return;
197602
- if ((this.commentId !== null || !this.renderedCurrent()) && this.buffer.trim() && this.failures < MAX_FAILURES) {
197603
- this.inFlight = this.send();
197604
- await this.inFlight;
197674
+ }, this.finalizeTimeoutMs);
197675
+ } catch (err) {
197676
+ this.abandon();
197677
+ this.safeWarn(`github poster: publish deadline failed on ${this.repo}#${this.issueNumber} (${String(err)})`);
197678
+ resolve();
197605
197679
  }
197606
- })();
197607
- await Promise.race([drain, deadline]);
197680
+ });
197681
+ await Promise.race([this.post(finalBody, deadlineAt), deadline]);
197608
197682
  } catch (err) {
197609
- try {
197610
- this.deps.log.warn(`github poster: final flush failed on ${this.repo}#${this.issueNumber} (${String(err)})`);
197611
- } catch {}
197683
+ if (!this.abandoned) this.safeWarn(`github poster: create failed on ${this.repo}#${this.issueNumber} (${String(err)})`);
197612
197684
  } finally {
197613
197685
  if (deadlineHandle !== void 0) try {
197614
197686
  this.sched.clearTimeout(deadlineHandle);
197615
197687
  } catch {}
197616
197688
  }
197617
197689
  }
197618
- schedule() {
197619
- if (this.abandoned || this.busy || this.timer !== null) return;
197620
- if (this.renderedCurrent()) return;
197621
- if (!this.hasSent) {
197622
- this.inFlight = this.send();
197690
+ async post(text, deadlineAt) {
197691
+ const body = this.render(text);
197692
+ const token = await this.deps.token();
197693
+ if (this.abandoned) return;
197694
+ if (this.sched.now() >= deadlineAt) {
197695
+ this.abandonTimedOut();
197623
197696
  return;
197624
197697
  }
197625
- const wait = Math.max(0, this.lastSendAt + this.editIntervalMs - this.sched.now());
197626
- this.timer = this.sched.setTimeout(() => {
197627
- this.timer = null;
197628
- if (this.finished) return;
197629
- this.inFlight = this.send();
197630
- }, wait);
197631
- }
197632
- /** One create-or-edit round; merges everything buffered at send time. */
197633
- async send() {
197634
- if (this.abandoned || this.busy) return;
197635
- this.busy = true;
197636
- const snapshot = this.buffer;
197698
+ const res = await (this.deps.fetchImpl ?? fetch)(`${this.deps.baseUrl ?? "https://api.github.com"}/repos/${this.repo}/issues/${this.issueNumber}/comments`, {
197699
+ method: "POST",
197700
+ headers: {
197701
+ authorization: `Bearer ${token}`,
197702
+ accept: "application/vnd.github+json",
197703
+ "content-type": "application/json",
197704
+ "x-github-api-version": "2022-11-28"
197705
+ },
197706
+ signal: this.abort.signal,
197707
+ body: JSON.stringify({ body })
197708
+ });
197637
197709
  try {
197638
- const body = this.render(snapshot);
197639
- const token = await this.deps.token();
197640
- if (this.abandoned) return;
197641
- if (this.commentId === null) {
197642
- const created = await this.request("POST", `/repos/${this.repo}/issues/${this.issueNumber}/comments`, token, body);
197643
- this.commentId = created.id;
197644
- this.deps.log.debug(`github poster: comment ${created.id} opened on ${this.repo}#${this.issueNumber}`);
197645
- } else await this.request("PATCH", `/repos/${this.repo}/issues/comments/${this.commentId}`, token, body);
197646
- this.postedBody = body;
197647
- this.failures = 0;
197648
- } catch (err) {
197649
- if (this.abandoned) return;
197650
- this.failures += 1;
197651
- this.deps.log.warn(`github poster: ${this.commentId === null ? "create" : "edit"} failed on ${this.repo}#${this.issueNumber} (${err.message?.slice(0, 200)})${this.failures >= MAX_FAILURES ? " — giving up for this turn" : ""}`);
197652
- } finally {
197653
- this.hasSent = true;
197654
- this.lastSendAt = this.sched.now();
197655
- this.busy = false;
197656
- if (!this.finished && !this.renderedCurrent() && this.failures < MAX_FAILURES) this.schedule();
197657
- }
197710
+ await res.body?.cancel();
197711
+ } catch {}
197712
+ if (!res.ok) throw new Error(`GitHub POST ${res.status}`);
197658
197713
  }
197659
197714
  render(text) {
197660
197715
  if (text.length + this.footer.length <= MAX_COMMENT_CHARS) {
@@ -197664,23 +197719,20 @@ var GithubCommentStream = class {
197664
197719
  const suffix = TRUNCATION_MARKER + this.footer;
197665
197720
  return `${truncatedMarkdownPrefix(text, Math.max(0, MAX_COMMENT_CHARS - suffix.length))}${suffix}`;
197666
197721
  }
197667
- renderedCurrent() {
197668
- return this.postedBody !== null && this.render(this.buffer) === this.postedBody;
197722
+ safeWarn(message) {
197723
+ try {
197724
+ this.deps.log.warn(message);
197725
+ } catch {}
197669
197726
  }
197670
- async request(method, path, token, body) {
197671
- const res = await (this.deps.fetchImpl ?? fetch)(`${this.deps.baseUrl ?? "https://api.github.com"}${path}`, {
197672
- method,
197673
- headers: {
197674
- authorization: `Bearer ${token}`,
197675
- accept: "application/vnd.github+json",
197676
- "content-type": "application/json",
197677
- "x-github-api-version": "2022-11-28"
197678
- },
197679
- signal: this.abort.signal,
197680
- body: JSON.stringify({ body })
197681
- });
197682
- if (!res.ok) throw new Error(`GitHub ${method} ${res.status}`);
197683
- return await res.json();
197727
+ abandon() {
197728
+ if (this.abandoned) return false;
197729
+ this.abandoned = true;
197730
+ this.abort.abort();
197731
+ return true;
197732
+ }
197733
+ abandonTimedOut() {
197734
+ if (!this.abandon()) return;
197735
+ this.safeWarn(`github poster: final publish timed out on ${this.repo}#${this.issueNumber}`);
197684
197736
  }
197685
197737
  };
197686
197738
  //#endregion
@@ -214910,6 +214962,9 @@ var Daemon = class Daemon {
214910
214962
  });
214911
214963
  gitCreds;
214912
214964
  gitCredServer;
214965
+ /** run/bin with the gh wrapper (multi-repo #457) — prepended to github-app
214966
+ * agents' PATH at host spawn; unset ⇒ shim write failed, spawn without it. */
214967
+ ghBinDir;
214913
214968
  agents = /* @__PURE__ */ new Map();
214914
214969
  fileAgents = /* @__PURE__ */ new Map();
214915
214970
  hosts = /* @__PURE__ */ new Map();
@@ -214998,9 +215053,21 @@ var Daemon = class Daemon {
214998
215053
  await this.gitCreds.get(agentId, reason);
214999
215054
  }
215000
215055
  });
215056
+ try {
215057
+ this.ghBinDir = writeGhShim(root, process.argv[1] ?? "");
215058
+ } catch (err) {
215059
+ this.log.warn(`gitcred: gh wrapper shim write failed — spawning agents without it (${formatErr(err)})`);
215060
+ }
215001
215061
  this.gitCredServer = new GitCredServer(this.gitCreds, gitcredSocketPath(root), {
215002
- info: (m) => this.log.info(m),
215003
- warn: (m) => this.log.warn(m)
215062
+ log: {
215063
+ info: (m) => this.log.info(m),
215064
+ warn: (m) => this.log.warn(m)
215065
+ },
215066
+ workspaceRepoOf: (agentId) => {
215067
+ const ws = this.agents.get(agentId)?.workspace;
215068
+ if (ws?.mode !== "git-repo" || !ws.gitRepo) return void 0;
215069
+ return gitRepoLabel(ws.gitRepo) ?? void 0;
215070
+ }
215004
215071
  });
215005
215072
  this.gitCredServer.start();
215006
215073
  probeGitVersion((m) => this.log.warn(m));
@@ -215731,17 +215798,26 @@ var Daemon = class Daemon {
215731
215798
  else {
215732
215799
  const runtime = this.runtimes[agent.runtime];
215733
215800
  if (!runtime) throw new Error(`runtime "${agent.runtime}" not available: not installed on this host, or absent from config.runtimes / the ACP registry`);
215801
+ const githubApp = agent.workspace.mode === "git-repo" && agent.workspace.gitCredential === "github-app";
215802
+ const env = {
215803
+ ...ghToken ? {
215804
+ GH_TOKEN: ghToken,
215805
+ AC_GH_TOKEN_INJECTED: ghToken
215806
+ } : {},
215807
+ ...agentChildEnv(agent),
215808
+ ...cpRuntimeEnv(agent),
215809
+ ...memoryProviderFor(agent, runtime).runtimeEnv(),
215810
+ ...githubApp ? sessionGitEnv(agent.id) : {}
215811
+ };
215812
+ if (githubApp && this.ghBinDir) {
215813
+ env.AC_AGENT_ID = agent.id;
215814
+ env.PATH = `${this.ghBinDir}:${env.PATH ?? process.env.PATH ?? ""}`;
215815
+ }
215734
215816
  host = new AcpHost(runtime, {
215735
215817
  onUpdate,
215736
215818
  onPermission: (sid, params) => this.onAcpPermission(agentId, sid, params),
215737
215819
  onElicit: (sid, params) => this.onAcpElicit(agentId, sid, params),
215738
- env: {
215739
- ...ghToken ? { GH_TOKEN: ghToken } : {},
215740
- ...agentChildEnv(agent),
215741
- ...cpRuntimeEnv(agent),
215742
- ...memoryProviderFor(agent, runtime).runtimeEnv(),
215743
- ...agent.workspace.mode === "git-repo" && agent.workspace.gitCredential === "github-app" ? sessionGitEnv(agent.id) : {}
215744
- },
215820
+ env,
215745
215821
  configPrefs: {
215746
215822
  model: agent.runtimeOverrides?.model,
215747
215823
  permissionMode: agent.permissionMode,
@@ -217472,7 +217548,7 @@ var Daemon = class Daemon {
217472
217548
  kind: "text",
217473
217549
  text: githubFinal
217474
217550
  });
217475
- if (p.github) await p.github.stream.finish(githubFinal).catch((err) => this.log.warn(`github poster: final flush failed (${formatErr(err)})`));
217551
+ if (p.github) await p.github.poster.publish(githubFinal).catch((err) => this.log.warn(`github poster: final publish failed (${formatErr(err)})`));
217476
217552
  this.pending.delete(sessionId);
217477
217553
  this.store.setSessionState(key, "idle", this.clock.now());
217478
217554
  this.emitSessionMetadataSnapshot({
@@ -218823,20 +218899,17 @@ var Daemon = class Daemon {
218823
218899
  }
218824
218900
  return this.dispatch(agentId, msg, target?.integrationId, void 0, void 0, void 0, githubReply);
218825
218901
  }
218826
- /** Build the per-turn GitHub final-answer selector and comment stream, tokened
218902
+ /** Build the per-turn GitHub final-answer selector and poster, tokened
218827
218903
  * via the repo-targeted gitcred mint (issues/PR write, no contents — never
218828
- * enters agent env). Attribution is fixed at turn creation so every edit has
218829
- * the same agent/session footer. */
218904
+ * enters agent env). Attribution is fixed at turn creation so the completed
218905
+ * comment carries the agent/session footer. */
218830
218906
  makeGithubReply(agentId, ref, sessionId) {
218831
218907
  const agent = this.agents.get(agentId);
218832
218908
  return {
218833
218909
  collector: new GithubReplyCollector(),
218834
- stream: new GithubCommentStream({
218910
+ poster: new GithubFinalPoster({
218835
218911
  token: async () => (await this.gitCreds.getPostToken(agentId, ref.repo)).token,
218836
- log: {
218837
- warn: (m) => this.log.warn(m),
218838
- debug: (m) => this.log.debug(m)
218839
- }
218912
+ log: { warn: (m) => this.log.warn(m) }
218840
218913
  }, ref.repo, ref.number, {
218841
218914
  agentName: agent?.name ?? agentId,
218842
218915
  agentUrl: this.agentLink(agentId),
@@ -219130,4 +219203,4 @@ async function runForeground(opts, deps = {}) {
219130
219203
  //#endregion
219131
219204
  export { ClientTransport as i, CP_SUBPROTOCOL as n, CP_WS_PATH as r, runForeground as t };
219132
219205
 
219133
- //# sourceMappingURL=run-foreground-BHU49ZSi.js.map
219206
+ //# sourceMappingURL=run-foreground-B8sw7WKS.js.map