chainlesschain 0.162.162 → 0.162.163
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.
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-ClI2ALgv.js → AIOps-BQ0kgfA1.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BDjRcJaK.js → ActionButton-Dr1MDANe.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DikSRsaT.js → Analytics-CZ5vsaXW.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-9mLhMFmz.js → AppLayout-BIBga881.js} +3 -3
- package/src/assets/web-panel/assets/{Artifacts-BSr88BrH.js → Artifacts-vpXHRU3u.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CdvyimOF.js → Audit-B8exuoub.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-D3_hCedh.js → BackgroundAgents-923evRRN.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Cyvv9RHf.js → Backup-sr_sswlQ.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BZM14kt8.js → BaseInput-CnW-4XKT.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DrQSokVz.js → Chat-FZCsTZIL.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DDPd9qT0.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-bFLYeWMV.js → Checkbox-DFrxB59H.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-7mN7xJXT.js → Codegen-_b6WEel8.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DAB5ZWm7.js → Col-CkbnnZ-K.js} +1 -1
- package/src/assets/web-panel/assets/{Community-B_1TymaK.js → Community-BxM_82zW.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-Bq1uSWLJ.js → Compact-DYinP45R.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CyiE1jEe.js → Compliance-DyXp3sSP.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Cpk0D7vm.js → Cowork-BEr2DhW7.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-d_Lv3rdB.js → Cron-B-Vwo1hN.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DMqhHjMR.js → Crosschain-DbLxKddd.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Br-oPuBw.js → DID-BsQ_LFw9.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CfLW_RaP.js → Dashboard-lVlXQHu5.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DZnTOExV.js → Dropdown-Beo_foO7.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DHIUHDv1.js → EmailListRenderer-DszHIXED.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bw3t3myu.js → FamilyGuardDashboard-DcAwoh81.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D9cpQPAY.js → Federation-CuJLa2eb.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Jdczr9Mr.js → FormItemContext-Cxd1lxhc.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BT8ZMpC5.js → GenericCardRenderer-BBvq5Jsw.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DjARip2b.js → Git-WqPKetx6.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BgWbSRcj.js → Governance-CWYtY3ke.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CfouJciM.js → Inference-BnX1iiNV.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-50l7mrAr.js → KnowledgeGraph-BJuM8GN5.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-rq50H84B.js → Logs-DqLMNntm.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DUPIEdQH.js → MarkdownRenderer-BqYwEYou.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BYgPDmHU.js → Marketplace-_vGfmhNl.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-5DVgcFdx.js → McpTools-BeJDOkDj.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-B70LPk7f.js → Memory-Da0EEZDO.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-De8ssA5Y.js → MobileBridge-DSuzcCCy.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-D0RAk4e8.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-RJsOTsIA.js → Mtc-BE9YgdA0.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-Crh-6BEK.js → MtcAudit-fC2BpO5S.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-wuoR5l3x.js → Multisig-D24Xxqw3.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-U2Mji4fZ.js → NLProgramming-Bs3oGvS8.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CdTZAyDH.js → Notes-BFjmAA-3.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C-FucWE7.js → NotificationSettings-BX_9Bhv0.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DCYFTeaI.js → OrderTableRenderer-wjkZwJXI.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-an3OF85L.js → Organization-DBcdsVEP.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-C-1vmYFe.js → Overflow-DDyaF9vC.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Cwh47xKg.js → P2P-BB78L4he.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Dm38GT0k.js → PdhVaultBrowser-DqTiZFum.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DS2Y8awD.js → Permissions-INbnWv4j.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BQNziA0I.js → PersonalDataHub-BST-VmTq.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-DF85L1ue.js → Pipeline-CE7_tS2k.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BuLornF-.js → Privacy-hK5L8dOB.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-C3Y31IwX.js → ProjectInit-BvkkUoDn.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BCPiI8aD.js → ProjectSettings-u7_EUtkm.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BZ6s4k6g.js → Projects-5Fmt_8Nw.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-D3Wp022X.js → Providers-B9eanarR.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CUJfQkH4.js → QrScannerModal-C0dX5N2f.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-UAc0EO8l.js → QuickAsk-CKTpG-PZ.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BaxKupcs.js → Recommend-Px_w_XQM.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C75NNnXZ.js → RemoteSession-Di0fnYkM.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-BVXynZAk.js → Reputation-vesnWjXt.js} +1 -1
- package/src/assets/web-panel/assets/{Row-C5MRZ0bi.js → Row-exbY6LkT.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DAKkEYka.js → RssFeed-B3O3x70t.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DvWv9YOA.js → Search-uRA0DhNz.js} +1 -1
- package/src/assets/web-panel/assets/{Security-B5peafW1.js → Security-CViqdH6K.js} +4 -4
- package/src/assets/web-panel/assets/{Services-UQmvPEaG.js → Services-CkXS97WN.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-C3bhWpuF.js → Skeleton-DxW5WYnE.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DfkKf9_H.js → Skills-CLxmGFse.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DqMSjlBM.js → Sla-Cr5fziXq.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-C-WbxWUX.js → SpeechSettings-H0JSb3cp.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CYuHZ4N2.js → SyncSettings-DMYWMae7.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CyeAyB9d.js → Tasks-CbYyDdF4.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BTCeQnpq.js → Templates-Cpc8JjiK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BifQuA9Y.js → Tenant-CRh77Jda.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-Bx_9Bocf.js → Terminal-4D8tNBO5.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BX0RFCOm.js → TimelineRenderer-D41jzzbG.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CGHrCmUS.js → Tokens-C_jWEy6b.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DlqaY4SO.js → Trigger-Dk1ZUhsy.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DfOmj-ay.js → Trust-CLVy4l7m.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B72jH5fE.js → UkeySign-C-FeqFDj.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BT-63fqv.js → VideoEditing-CqVZzyMm.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Dsul3sHr.js → Wallet-DX9BW9pu.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Izkwwdgy.js → WebAuthn-7hQDpWw_.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-D3FHSiH1.js → WorkflowEditor-6XqUAO0j.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CmVz5z3U.js → chat-5R_vkaJG.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CuLgyJBp.js → colors-huUPZdC-.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Ml1YkXpw.js → compact-item-CfJlgisE.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BvjJ48bs.js → createContext-jO8heofr.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DeIrCfH-.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DVC0157C.js → hasIn-DY8Vxqx7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJyN28Et.js → index-A48PtXw9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CudmwNBV.js → index-B300RPnT.js} +3 -3
- package/src/assets/web-panel/assets/{index-BbepULSc.js → index-B74OXFxC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DYXHFr4r.js → index-BAON7TzU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BzG-x9p4.js → index-BCqxyvqp.js} +1 -1
- package/src/assets/web-panel/assets/{index-wvb_FtaV.js → index-BHE8OIls.js} +1 -1
- package/src/assets/web-panel/assets/{index-QXzlT9KD.js → index-BQS-uPOK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DukDVXCv.js → index-BRXOP09X.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFYVQMvu.js → index-BYLBPpt8.js} +1 -1
- package/src/assets/web-panel/assets/{index-BydjdZnl.js → index-BfCx4pFT.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmFNvNLZ.js → index-BliaT02i.js} +1 -1
- package/src/assets/web-panel/assets/{index-KMqhcF7l.js → index-BmqGerpC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ck_52dz_.js → index-BsBtRWbA.js} +1 -1
- package/src/assets/web-panel/assets/{index-C229ABfy.js → index-BtEXSoPL.js} +1 -1
- package/src/assets/web-panel/assets/index-BxUKMRpP.js +1 -0
- package/src/assets/web-panel/assets/{index-C_H_S-DY.js → index-Bxax9vQ6.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0S8NzO6.js → index-C2iVSFpy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BjHBpRYZ.js → index-CA6sO5VD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bzr0rB5T.js → index-CA90TZxG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDGaOWBV.js → index-CLhBh-qE.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dt5KMjwJ.js → index-CRElRhyS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BbPtzB8M.js → index-Cbe9G0V4.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_vRgTl9.js → index-ChvHubX3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpFJHiTv.js → index-Cis_Ip7H.js} +1 -1
- package/src/assets/web-panel/assets/{index-KjgKdZe-.js → index-Cjxbu68P.js} +1 -1
- package/src/assets/web-panel/assets/{index-vG4XSJ1d.js → index-CzKmteNS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUt8kTIB.js → index-D0pet5Uy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfuKP4qe.js → index-D1VLuww0.js} +1 -1
- package/src/assets/web-panel/assets/{index-4SBta3d7.js → index-DXBcvUnf.js} +1 -1
- package/src/assets/web-panel/assets/{index-DC_OI9H2.js → index-Db1cLWir.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuGszaWY.js → index-DoI2sZm7.js} +1 -1
- package/src/assets/web-panel/assets/index-DscU9iPI.js +1 -0
- package/src/assets/web-panel/assets/{index-4wcgvPoF.js → index-KRHroryI.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3lD5qap.js → index-d1xkbtNz.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_EvG_GW.js → index-dKx57io7.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXkJH-lK.js → index-j3O2EEJe.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiC6dQZG.js → index-p85YaaKw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cw0Rjy87.js → index-v3pTaEQm.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_XuRgGn.js → index-vXs3B8GJ.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BDgd2YZ8.js → initDefaultProps-MCB7-raZ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-ErtrXdYe.js → motion-UWDF9swV.js} +1 -1
- package/src/assets/web-panel/assets/{move-_GPPNCgs.js → move-CsTmgXZU.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DRLL-8V2.js → mtc-parser-fQy6KGLg.js} +1 -1
- package/src/assets/web-panel/assets/{omit-p86sl023.js → omit-woPnEjd1.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BTu7cMLH.js → pickAttrs-Bs2dZ1gf.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BEKz6Isr.js → placementArrow-C7EVgPkg.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Ck42c9MP.js → responsiveObserve-BH2kYCTN.js} +1 -1
- package/src/assets/web-panel/assets/{slide-1CEvuPsB.js → slide-BFBP2V_e.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BDfdx1GJ.js → statusUtils-xdDxDYse.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B4K46m5w.js → styleChecker-aAWgPPQf.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DoIVpJNq.js → useFlexGapSupport-DkGSAWoO.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DdAWi-ln.js → useFs-BrOih40N.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-iUu6k8dx.js → usePersonalDataHub-eIYrgMqO.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DZz4R6Tw.js → vnode-B8lutNSE.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DXlf9sA3.js → zoom-CffNre7V.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +254 -23
- package/src/commands/agent.js +105 -11
- package/src/commands/context.js +125 -14
- package/src/commands/hook.js +181 -7
- package/src/commands/plugin.js +239 -1
- package/src/commands/review.js +347 -2
- package/src/data/changelog.json +9 -4
- package/src/gateways/ws/remote-session-protocol.js +39 -0
- package/src/gateways/ws/session-protocol.js +5 -2
- package/src/lib/agent-schedule-store.js +289 -10
- package/src/lib/channels/channel-manager.js +54 -2
- package/src/lib/cli-context-engineering.js +9 -2
- package/src/lib/context-breakdown.js +116 -0
- package/src/lib/doctor-checkup.js +52 -0
- package/src/lib/hook-event-log.cjs +193 -0
- package/src/lib/interaction-adapter.js +34 -4
- package/src/lib/json-schema-output.js +46 -12
- package/src/lib/json-schema-validate.js +143 -3
- package/src/lib/plugin-runtime/capability-consent.js +175 -0
- package/src/lib/plugin-runtime/policy.js +62 -0
- package/src/lib/plugin-runtime/scopes.js +20 -4
- package/src/lib/project-instructions.js +128 -0
- package/src/lib/repl-goal.js +132 -0
- package/src/lib/repl-rewind.js +52 -0
- package/src/lib/schedule-planner.js +17 -1
- package/src/lib/settings-hook-events.cjs +13 -0
- package/src/lib/settings-loader.cjs +42 -0
- package/src/lib/side-effect-ledger-store.js +88 -0
- package/src/lib/side-effect-ledger.js +296 -0
- package/src/lib/sub-agent-context.js +74 -5
- package/src/lib/subagent-inheritance.js +112 -0
- package/src/lib/turn-binding-store.js +119 -0
- package/src/repl/agent-repl.js +136 -0
- package/src/repl/permission-tier.js +28 -0
- package/src/runtime/agent-core.js +376 -10
- package/src/runtime/coding-agent-contract-shared.cjs +60 -1
- package/src/runtime/headless-runner.js +267 -0
- package/src/runtime/headless-stream.js +60 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-HwJR-C6B.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-D10VISps.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DhiGrbsh.js +0 -1
- package/src/assets/web-panel/assets/index-B7a1TrU6.js +0 -1
- package/src/assets/web-panel/assets/index-C2B1KRja.js +0 -1
package/src/commands/agenda.js
CHANGED
|
@@ -7,20 +7,24 @@
|
|
|
7
7
|
* cc agenda list [--json] show all scheduled entries
|
|
8
8
|
* cc agenda run [--json] [--dry-run] fire everything due right now
|
|
9
9
|
* cc agenda cancel <id> remove one entry
|
|
10
|
+
* cc agenda prune [--older-than N] drop finished entries
|
|
10
11
|
*
|
|
11
12
|
* `run` for each due entry:
|
|
12
13
|
* - wakeup → spawn `cc agent -p <prompt>`, mark fired
|
|
13
14
|
* - cron → spawn `cc agent -p <prompt>`, advance to next fire time
|
|
14
|
-
* - monitor →
|
|
15
|
-
*
|
|
15
|
+
* - monitor → check its source (a shell <command>'s output, a <watchFile>'s
|
|
16
|
+
* content / appearance / modification, or a <watchUrl>'s response
|
|
17
|
+
* body / 2xx); if it matches stop_when, send the notification and
|
|
18
|
+
* stop; else re-arm for the next interval
|
|
16
19
|
*/
|
|
17
20
|
|
|
18
21
|
import { spawn, execSync } from "node:child_process";
|
|
22
|
+
import { readFileSync, statSync } from "node:fs";
|
|
19
23
|
import { fileURLToPath } from "node:url";
|
|
20
24
|
import chalk from "chalk";
|
|
21
25
|
import { AgentScheduleStore } from "../lib/agent-schedule-store.js";
|
|
22
26
|
import { sendAgentNotification } from "../lib/agent-notify.js";
|
|
23
|
-
import { nextWakeupAt } from "../lib/schedule-planner.js";
|
|
27
|
+
import { nextWakeupAt, partitionSchedule } from "../lib/schedule-planner.js";
|
|
24
28
|
|
|
25
29
|
const BIN_PATH = fileURLToPath(
|
|
26
30
|
new URL("../../bin/chainlesschain.js", import.meta.url),
|
|
@@ -58,6 +62,20 @@ export function registerAgendaCommand(program) {
|
|
|
58
62
|
.action((id, options) => {
|
|
59
63
|
process.exitCode = runAgendaCancel(id, options);
|
|
60
64
|
});
|
|
65
|
+
|
|
66
|
+
cmd
|
|
67
|
+
.command("prune")
|
|
68
|
+
.description(
|
|
69
|
+
"Remove finished entries (fired / matched / exhausted / expired)",
|
|
70
|
+
)
|
|
71
|
+
.option(
|
|
72
|
+
"--older-than <seconds>",
|
|
73
|
+
"Only prune entries that finished more than N seconds ago",
|
|
74
|
+
)
|
|
75
|
+
.option("--json", "Machine-readable JSON output")
|
|
76
|
+
.action((options) => {
|
|
77
|
+
process.exitCode = runAgendaPrune(options);
|
|
78
|
+
});
|
|
61
79
|
}
|
|
62
80
|
|
|
63
81
|
export function runAgendaList(options = {}, _deps = {}) {
|
|
@@ -80,17 +98,30 @@ export function runAgendaList(options = {}, _deps = {}) {
|
|
|
80
98
|
log("");
|
|
81
99
|
log(chalk.bold(" Agenda:\n"));
|
|
82
100
|
for (const e of entries) {
|
|
101
|
+
const monitorWhat =
|
|
102
|
+
e.source === "file"
|
|
103
|
+
? `file ${e.watchFile}`
|
|
104
|
+
: e.source === "http"
|
|
105
|
+
? `url ${e.watchUrl}`
|
|
106
|
+
: `every ${Math.round(e.intervalMs / 1000)}s`;
|
|
83
107
|
const when =
|
|
84
108
|
e.kind === "wakeup"
|
|
85
109
|
? new Date(e.dueAt).toISOString()
|
|
86
110
|
: e.kind === "cron"
|
|
87
111
|
? `${e.cron} (next ${new Date(e.nextAt).toISOString()})`
|
|
88
|
-
:
|
|
112
|
+
: `${monitorWhat} (next ${new Date(e.nextAt).toISOString()})`;
|
|
113
|
+
const expiry =
|
|
114
|
+
e.expiresAt != null
|
|
115
|
+
? chalk.dim(` · expires ${new Date(e.expiresAt).toISOString()}`)
|
|
116
|
+
: "";
|
|
117
|
+
const policy = e.runPolicy
|
|
118
|
+
? chalk.dim(` · ${describePolicy(e.runPolicy)}`)
|
|
119
|
+
: "";
|
|
89
120
|
log(
|
|
90
121
|
` ${chalk.cyan(e.kind.padEnd(8))} ${chalk.dim(e.id.slice(0, 8))} ` +
|
|
91
|
-
`${statusBadge(e.status)} ${e.label || truncate(e.prompt || e.command, 40)}`,
|
|
122
|
+
`${statusBadge(e.status)} ${e.label || truncate(e.prompt || e.command || e.watchFile || e.watchUrl, 40)}`,
|
|
92
123
|
);
|
|
93
|
-
log(` ${chalk.dim(when)}`);
|
|
124
|
+
log(` ${chalk.dim(when)}${expiry}${policy}`);
|
|
94
125
|
}
|
|
95
126
|
if (nextAt != null) {
|
|
96
127
|
const rel = Math.max(0, Math.round((nextAt - now) / 1000));
|
|
@@ -120,6 +151,54 @@ export function runAgendaCancel(id, options = {}, _deps = {}) {
|
|
|
120
151
|
return 2;
|
|
121
152
|
}
|
|
122
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Remove finished (terminal) entries so the store does not grow without bound.
|
|
156
|
+
* `--older-than <seconds>` keeps entries that finished more recently.
|
|
157
|
+
*/
|
|
158
|
+
export function runAgendaPrune(options = {}, _deps = {}) {
|
|
159
|
+
const log = _deps.log || ((m) => console.log(m));
|
|
160
|
+
const store = _deps.store || new AgentScheduleStore();
|
|
161
|
+
const now = _deps.now ? _deps.now() : Date.now();
|
|
162
|
+
|
|
163
|
+
let before = Infinity;
|
|
164
|
+
if (options.olderThan != null) {
|
|
165
|
+
const secs = Number(options.olderThan);
|
|
166
|
+
if (!Number.isFinite(secs) || secs < 0) {
|
|
167
|
+
log(chalk.red(`\n ✖ invalid --older-than: ${options.olderThan}\n`));
|
|
168
|
+
return 2;
|
|
169
|
+
}
|
|
170
|
+
before = now - secs * 1000;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const removed = store.pruneTerminal({ before });
|
|
174
|
+
if (options.json) {
|
|
175
|
+
log(
|
|
176
|
+
JSON.stringify(
|
|
177
|
+
{
|
|
178
|
+
pruned: removed.map((e) => ({
|
|
179
|
+
id: e.id,
|
|
180
|
+
kind: e.kind,
|
|
181
|
+
status: e.status,
|
|
182
|
+
})),
|
|
183
|
+
},
|
|
184
|
+
null,
|
|
185
|
+
2,
|
|
186
|
+
),
|
|
187
|
+
);
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
if (removed.length === 0) {
|
|
191
|
+
log(chalk.gray("\n No finished entries to prune.\n"));
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
194
|
+
log(
|
|
195
|
+
chalk.green(
|
|
196
|
+
`\n ✓ Pruned ${removed.length} finished entr${removed.length === 1 ? "y" : "ies"}\n`,
|
|
197
|
+
),
|
|
198
|
+
);
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
123
202
|
/**
|
|
124
203
|
* Fire due entries. Returns 0 unless a monitor command / agent spawn failed in
|
|
125
204
|
* a way worth signalling; JSON summary lists every action taken.
|
|
@@ -129,8 +208,16 @@ export async function runAgendaRun(options = {}, _deps = {}) {
|
|
|
129
208
|
const store = _deps.store || new AgentScheduleStore();
|
|
130
209
|
const spawnAgent = _deps.spawnAgent || defaultSpawnAgent;
|
|
131
210
|
const runCommand = _deps.runCommand || defaultRunCommand;
|
|
211
|
+
const readWatchedFile = _deps.readWatchedFile || defaultReadWatchedFile;
|
|
212
|
+
const fetchUrl = _deps.fetchUrl || defaultFetchUrl;
|
|
132
213
|
const notify = _deps.notify || sendAgentNotification;
|
|
133
|
-
const
|
|
214
|
+
const now = _deps.now ? _deps.now() : Date.now();
|
|
215
|
+
// Retire expired entries BEFORE firing due ones — an expired task never gets
|
|
216
|
+
// a final fire (schedule-planner semantics). Dry-run inspects without mutating.
|
|
217
|
+
const retired = options.dryRun
|
|
218
|
+
? partitionSchedule(store.list(), { now }).expired
|
|
219
|
+
: store.retireExpired(now);
|
|
220
|
+
const due = store.due(null, now);
|
|
134
221
|
const actions = [];
|
|
135
222
|
|
|
136
223
|
for (const entry of due) {
|
|
@@ -140,26 +227,65 @@ export async function runAgendaRun(options = {}, _deps = {}) {
|
|
|
140
227
|
}
|
|
141
228
|
try {
|
|
142
229
|
if (entry.kind === "wakeup") {
|
|
143
|
-
await spawnAgent(entry.prompt);
|
|
230
|
+
await spawnAgent(entry.prompt, entry.runPolicy);
|
|
144
231
|
store.markWakeupFired(entry.id);
|
|
145
232
|
actions.push({ id: entry.id, kind: "wakeup", action: "fired" });
|
|
146
233
|
} else if (entry.kind === "cron") {
|
|
147
|
-
await spawnAgent(entry.prompt);
|
|
234
|
+
await spawnAgent(entry.prompt, entry.runPolicy);
|
|
148
235
|
store.advanceCron(entry.id);
|
|
149
236
|
actions.push({ id: entry.id, kind: "cron", action: "fired" });
|
|
150
237
|
} else if (entry.kind === "monitor") {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
238
|
+
// Three sources: a shell command (match its output), a watched file
|
|
239
|
+
// (match its content, or — with no stopWhen — fire when it appears),
|
|
240
|
+
// or an HTTP endpoint (match its response body, or fire on 2xx).
|
|
241
|
+
let output = "";
|
|
242
|
+
let matched = false;
|
|
243
|
+
let mtimeMs = null;
|
|
244
|
+
if (entry.source === "file") {
|
|
245
|
+
const file = await readWatchedFile(entry.watchFile);
|
|
246
|
+
output = file.content;
|
|
247
|
+
mtimeMs = file.mtimeMs ?? null;
|
|
248
|
+
if (entry.watchChange) {
|
|
249
|
+
// Fire once the file's mtime advances past the baseline recorded on
|
|
250
|
+
// the first check. The first check only establishes the baseline.
|
|
251
|
+
matched =
|
|
252
|
+
entry.lastMtimeMs != null &&
|
|
253
|
+
mtimeMs != null &&
|
|
254
|
+
mtimeMs > entry.lastMtimeMs;
|
|
255
|
+
} else {
|
|
256
|
+
matched = entry.stopWhen
|
|
257
|
+
? new RegExp(entry.stopWhen).test(file.content)
|
|
258
|
+
: file.exists; // no pattern → the file appearing is the signal
|
|
259
|
+
}
|
|
260
|
+
} else if (entry.source === "http") {
|
|
261
|
+
const res = await fetchUrl(entry.watchUrl);
|
|
262
|
+
output = res.body;
|
|
263
|
+
matched = entry.stopWhen
|
|
264
|
+
? new RegExp(entry.stopWhen).test(res.body)
|
|
265
|
+
: res.ok; // no pattern → a 2xx response is the signal
|
|
266
|
+
} else {
|
|
267
|
+
output = await runCommand(entry.command);
|
|
268
|
+
matched = entry.stopWhen
|
|
269
|
+
? new RegExp(entry.stopWhen).test(output)
|
|
270
|
+
: false;
|
|
271
|
+
}
|
|
155
272
|
if (matched) {
|
|
273
|
+
const what =
|
|
274
|
+
entry.source === "file"
|
|
275
|
+
? entry.watchFile
|
|
276
|
+
: entry.source === "http"
|
|
277
|
+
? entry.watchUrl
|
|
278
|
+
: entry.command;
|
|
156
279
|
await notify({
|
|
157
|
-
title: entry.notify?.title || `Monitor matched: ${
|
|
280
|
+
title: entry.notify?.title || `Monitor matched: ${what}`,
|
|
158
281
|
body: truncate(output, 500),
|
|
159
282
|
level: "success",
|
|
160
283
|
});
|
|
161
284
|
}
|
|
162
|
-
const updated = store.recordMonitorCheck(entry.id, {
|
|
285
|
+
const updated = store.recordMonitorCheck(entry.id, {
|
|
286
|
+
matched,
|
|
287
|
+
mtimeMs,
|
|
288
|
+
});
|
|
163
289
|
actions.push({
|
|
164
290
|
id: entry.id,
|
|
165
291
|
kind: "monitor",
|
|
@@ -177,11 +303,25 @@ export async function runAgendaRun(options = {}, _deps = {}) {
|
|
|
177
303
|
}
|
|
178
304
|
}
|
|
179
305
|
|
|
306
|
+
const retiredSummary = retired.map((e) => ({ id: e.id, kind: e.kind }));
|
|
180
307
|
if (options.json) {
|
|
181
|
-
log(
|
|
182
|
-
|
|
308
|
+
log(
|
|
309
|
+
JSON.stringify(
|
|
310
|
+
{ due: due.length, retired: retiredSummary, actions },
|
|
311
|
+
null,
|
|
312
|
+
2,
|
|
313
|
+
),
|
|
314
|
+
);
|
|
315
|
+
} else if (actions.length === 0 && retired.length === 0) {
|
|
183
316
|
log(chalk.gray(" Nothing due.\n"));
|
|
184
317
|
} else {
|
|
318
|
+
for (const e of retired) {
|
|
319
|
+
log(
|
|
320
|
+
chalk.gray(
|
|
321
|
+
` ${e.kind} ${e.id.slice(0, 8)}: ${options.dryRun ? "would-expire" : "expired"}`,
|
|
322
|
+
),
|
|
323
|
+
);
|
|
324
|
+
}
|
|
185
325
|
for (const a of actions) {
|
|
186
326
|
const colour = a.action === "error" ? chalk.red : chalk.green;
|
|
187
327
|
log(colour(` ${a.kind} ${a.id.slice(0, 8)}: ${a.action}`));
|
|
@@ -192,13 +332,58 @@ export async function runAgendaRun(options = {}, _deps = {}) {
|
|
|
192
332
|
|
|
193
333
|
// ─── default effectful deps (overridable in tests) ─────────────────────────
|
|
194
334
|
|
|
195
|
-
|
|
335
|
+
/**
|
|
336
|
+
* Build the `cc agent` argv for a scheduled prompt, appending the entry's
|
|
337
|
+
* per-task run policy as real `cc agent` flags. A missing / empty policy adds
|
|
338
|
+
* nothing, so an unpolicied task spawns exactly `cc agent -p <prompt>` as
|
|
339
|
+
* before (byte-identical). Exported for unit tests.
|
|
340
|
+
*/
|
|
341
|
+
export function buildAgentArgs(prompt, policy = null) {
|
|
342
|
+
const args = ["agent", "-p", prompt];
|
|
343
|
+
if (policy && typeof policy === "object") {
|
|
344
|
+
if (
|
|
345
|
+
typeof policy.permissionMode === "string" &&
|
|
346
|
+
policy.permissionMode.length > 0
|
|
347
|
+
) {
|
|
348
|
+
args.push("--permission-mode", policy.permissionMode);
|
|
349
|
+
}
|
|
350
|
+
if (policy.worktree === true) args.push("--worktree");
|
|
351
|
+
if (Number.isFinite(policy.maxTurns) && policy.maxTurns > 0) {
|
|
352
|
+
args.push("--max-turns", String(policy.maxTurns));
|
|
353
|
+
}
|
|
354
|
+
if (
|
|
355
|
+
typeof policy.goalCondition === "string" &&
|
|
356
|
+
policy.goalCondition.length > 0
|
|
357
|
+
) {
|
|
358
|
+
args.push("--goal-condition", policy.goalCondition);
|
|
359
|
+
if (Number.isFinite(policy.maxOuterTurns) && policy.maxOuterTurns > 0) {
|
|
360
|
+
args.push("--max-outer-turns", String(policy.maxOuterTurns));
|
|
361
|
+
}
|
|
362
|
+
if (Number.isFinite(policy.goalMaxTokens) && policy.goalMaxTokens > 0) {
|
|
363
|
+
args.push("--goal-max-tokens", String(policy.goalMaxTokens));
|
|
364
|
+
}
|
|
365
|
+
if (Number.isFinite(policy.goalMaxCost) && policy.goalMaxCost > 0) {
|
|
366
|
+
args.push("--goal-max-cost", String(policy.goalMaxCost));
|
|
367
|
+
}
|
|
368
|
+
if (Number.isFinite(policy.goalMaxTime) && policy.goalMaxTime > 0) {
|
|
369
|
+
args.push("--goal-max-time", String(policy.goalMaxTime));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return args;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function defaultSpawnAgent(prompt, policy = null) {
|
|
196
377
|
return new Promise((resolve, reject) => {
|
|
197
|
-
const child = spawn(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
378
|
+
const child = spawn(
|
|
379
|
+
process.execPath,
|
|
380
|
+
[BIN_PATH, ...buildAgentArgs(prompt, policy)],
|
|
381
|
+
{
|
|
382
|
+
stdio: "ignore",
|
|
383
|
+
detached: false,
|
|
384
|
+
env: process.env,
|
|
385
|
+
},
|
|
386
|
+
);
|
|
202
387
|
child.on("error", reject);
|
|
203
388
|
child.on("close", (code) =>
|
|
204
389
|
code === 0 || code == null
|
|
@@ -221,6 +406,40 @@ function defaultRunCommand(command) {
|
|
|
221
406
|
}
|
|
222
407
|
}
|
|
223
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Read a watched file → { exists, content, mtimeMs }. A missing file is not an
|
|
411
|
+
* error. mtimeMs feeds watchChange monitors (fire when the file is modified).
|
|
412
|
+
*/
|
|
413
|
+
function defaultReadWatchedFile(filePath) {
|
|
414
|
+
try {
|
|
415
|
+
const content = readFileSync(filePath, "utf-8");
|
|
416
|
+
let mtimeMs = null;
|
|
417
|
+
try {
|
|
418
|
+
mtimeMs = statSync(filePath).mtimeMs;
|
|
419
|
+
} catch {
|
|
420
|
+
/* content read but stat failed — treat mtime as unknown */
|
|
421
|
+
}
|
|
422
|
+
return { exists: true, content, mtimeMs };
|
|
423
|
+
} catch {
|
|
424
|
+
return { exists: false, content: "", mtimeMs: null };
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* GET a watched URL → { ok, status, body }. Any failure (network, timeout,
|
|
430
|
+
* non-2xx) is not an error here — it just yields ok:false so the monitor
|
|
431
|
+
* re-arms and tries again next interval.
|
|
432
|
+
*/
|
|
433
|
+
async function defaultFetchUrl(url) {
|
|
434
|
+
try {
|
|
435
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(30000) });
|
|
436
|
+
const body = await res.text();
|
|
437
|
+
return { ok: res.ok, status: res.status, body };
|
|
438
|
+
} catch {
|
|
439
|
+
return { ok: false, status: 0, body: "" };
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
224
443
|
function statusBadge(status) {
|
|
225
444
|
switch (status) {
|
|
226
445
|
case "pending":
|
|
@@ -231,6 +450,8 @@ function statusBadge(status) {
|
|
|
231
450
|
return chalk.cyan(status);
|
|
232
451
|
case "exhausted":
|
|
233
452
|
return chalk.yellow(status);
|
|
453
|
+
case "expired":
|
|
454
|
+
return chalk.gray(status);
|
|
234
455
|
default:
|
|
235
456
|
return status;
|
|
236
457
|
}
|
|
@@ -240,3 +461,13 @@ function truncate(text, n) {
|
|
|
240
461
|
const s = String(text || "");
|
|
241
462
|
return s.length > n ? s.slice(0, n) + "…" : s;
|
|
242
463
|
}
|
|
464
|
+
|
|
465
|
+
/** Compact human summary of a scheduled entry's per-task run policy. */
|
|
466
|
+
function describePolicy(policy) {
|
|
467
|
+
const parts = [];
|
|
468
|
+
if (policy.permissionMode) parts.push(policy.permissionMode);
|
|
469
|
+
if (policy.worktree === true) parts.push("worktree");
|
|
470
|
+
if (Number.isFinite(policy.maxTurns)) parts.push(`≤${policy.maxTurns} turns`);
|
|
471
|
+
if (policy.goalCondition) parts.push(`goal: ${policy.goalCondition}`);
|
|
472
|
+
return parts.join(", ");
|
|
473
|
+
}
|
package/src/commands/agent.js
CHANGED
|
@@ -204,6 +204,10 @@ export function registerAgentCommand(program) {
|
|
|
204
204
|
"--otlp <file>",
|
|
205
205
|
"Write the agent run's OpenTelemetry spans (model/tool/retry) as OTLP/JSON to <file>",
|
|
206
206
|
)
|
|
207
|
+
.option(
|
|
208
|
+
"--otlp-content",
|
|
209
|
+
"Include prompt/response CONTENT in --otlp spans (off by default; content is redacted unless this is set)",
|
|
210
|
+
)
|
|
207
211
|
.option(
|
|
208
212
|
"--auto-rewake",
|
|
209
213
|
"When an async `asyncRewake` hook (e.g. a background test) fails at turn end, re-engage the agent to fix it (bounded by --max-rewakes)",
|
|
@@ -311,6 +315,30 @@ export function registerAgentCommand(program) {
|
|
|
311
315
|
"--goal-assess",
|
|
312
316
|
"After the run, ask the model to assess goal progress and persist it (opt-in; costs one extra completion; requires --goal)",
|
|
313
317
|
)
|
|
318
|
+
.option(
|
|
319
|
+
"--goal-condition <spec>",
|
|
320
|
+
"Session completion condition; re-drives outer turns until met. Prefix exit-zero:/file-exists:/contains:/regex: (deterministic) or model:<desc> (model-judged)",
|
|
321
|
+
)
|
|
322
|
+
.option(
|
|
323
|
+
"--max-outer-turns <n>",
|
|
324
|
+
"Max outer re-drive turns for --goal-condition (default 10, hard cap 100)",
|
|
325
|
+
(v) => parseInt(v, 10),
|
|
326
|
+
)
|
|
327
|
+
.option(
|
|
328
|
+
"--goal-max-tokens <n>",
|
|
329
|
+
"Token budget across --goal-condition outer turns",
|
|
330
|
+
(v) => parseInt(v, 10),
|
|
331
|
+
)
|
|
332
|
+
.option(
|
|
333
|
+
"--goal-max-cost <usd>",
|
|
334
|
+
"Cost budget (USD) across --goal-condition outer turns",
|
|
335
|
+
(v) => parseFloat(v),
|
|
336
|
+
)
|
|
337
|
+
.option(
|
|
338
|
+
"--goal-max-time <ms>",
|
|
339
|
+
"Wall-clock budget (ms) across --goal-condition outer turns",
|
|
340
|
+
(v) => parseInt(v, 10),
|
|
341
|
+
)
|
|
314
342
|
.option(
|
|
315
343
|
"--mcp-config <file>",
|
|
316
344
|
"Load ad-hoc MCP servers from a JSON file for this run (headless); their tools become callable (mcp__<server>__<tool>)",
|
|
@@ -871,6 +899,11 @@ export function registerAgentCommand(program) {
|
|
|
871
899
|
maxCostUsd,
|
|
872
900
|
priceTable,
|
|
873
901
|
chatFn: fallbackChatFn,
|
|
902
|
+
// --json-schema: structured output for stream-INPUT mode. The schema
|
|
903
|
+
// is resolved + meta-validated up front, its output contract injected
|
|
904
|
+
// into the system prompt, and a per-turn `structured_result` event is
|
|
905
|
+
// appended after each turn (parity with single-prompt --json-schema).
|
|
906
|
+
jsonSchema: options.jsonSchema || null,
|
|
874
907
|
});
|
|
875
908
|
} catch (err) {
|
|
876
909
|
process.stderr.write(`Error: ${err.message}\n`);
|
|
@@ -1001,6 +1034,19 @@ export function registerAgentCommand(program) {
|
|
|
1001
1034
|
}
|
|
1002
1035
|
const { runAgentHeadless, parseToolList } =
|
|
1003
1036
|
await import("../runtime/headless-runner.js");
|
|
1037
|
+
// --goal-condition: validate the spec up front so a bad prefix fails
|
|
1038
|
+
// fast (before any model call) with a clear message.
|
|
1039
|
+
if (options.goalCondition) {
|
|
1040
|
+
try {
|
|
1041
|
+
const { parseGoalCondition } =
|
|
1042
|
+
await import("../lib/goal-condition-engine.js");
|
|
1043
|
+
parseGoalCondition(options.goalCondition);
|
|
1044
|
+
} catch (e) {
|
|
1045
|
+
process.stderr.write(`--goal-condition: ${e.message}\n`);
|
|
1046
|
+
await _finishWorktree();
|
|
1047
|
+
process.exit(1);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1004
1050
|
const maxTurns = options.maxTurns
|
|
1005
1051
|
? parseInt(options.maxTurns, 10)
|
|
1006
1052
|
: undefined;
|
|
@@ -1027,6 +1073,9 @@ export function registerAgentCommand(program) {
|
|
|
1027
1073
|
autoCheckpoint,
|
|
1028
1074
|
// --otlp <file>: capture + export the run's OTel spans as OTLP/JSON.
|
|
1029
1075
|
otlp: options.otlp || null,
|
|
1076
|
+
// --otlp-content: opt in to emitting prompt/response CONTENT in those
|
|
1077
|
+
// spans. Default false → content stays redacted (privacy by default).
|
|
1078
|
+
otlpContent: options.otlpContent === true,
|
|
1030
1079
|
// --auto-rewake / --max-rewakes: bounded re-drive on async-hook failure.
|
|
1031
1080
|
// --max-rewakes implies --auto-rewake (a positive budget turns it on).
|
|
1032
1081
|
autoRewake:
|
|
@@ -1052,6 +1101,21 @@ export function registerAgentCommand(program) {
|
|
|
1052
1101
|
goal: options.goal,
|
|
1053
1102
|
// --goal-assess: run-end LLM progress assessment (Phase 2)
|
|
1054
1103
|
goalAssess: options.goalAssess === true,
|
|
1104
|
+
// --goal-condition: session-level completion-condition engine — drives
|
|
1105
|
+
// outer turns until the condition is met or a budget is exhausted.
|
|
1106
|
+
goalCondition: options.goalCondition || null,
|
|
1107
|
+
maxOuterTurns: Number.isFinite(options.maxOuterTurns)
|
|
1108
|
+
? options.maxOuterTurns
|
|
1109
|
+
: undefined,
|
|
1110
|
+
goalMaxTokens: Number.isFinite(options.goalMaxTokens)
|
|
1111
|
+
? options.goalMaxTokens
|
|
1112
|
+
: undefined,
|
|
1113
|
+
goalMaxCostUsd: Number.isFinite(options.goalMaxCost)
|
|
1114
|
+
? options.goalMaxCost
|
|
1115
|
+
: undefined,
|
|
1116
|
+
goalMaxTimeMs: Number.isFinite(options.goalMaxTime)
|
|
1117
|
+
? options.goalMaxTime
|
|
1118
|
+
: undefined,
|
|
1055
1119
|
// --mcp-config: connect ad-hoc MCP servers + expose their tools
|
|
1056
1120
|
mcpConfig: options.mcpConfig || null,
|
|
1057
1121
|
// --no-mcp: skip registered (cc mcp add) auto-connect servers
|
|
@@ -1081,21 +1145,51 @@ export function registerAgentCommand(program) {
|
|
|
1081
1145
|
chatFn: fallbackChatFn,
|
|
1082
1146
|
};
|
|
1083
1147
|
|
|
1084
|
-
// --json-schema: structured output
|
|
1085
|
-
//
|
|
1086
|
-
//
|
|
1087
|
-
//
|
|
1148
|
+
// --json-schema: structured output. Accepts a file path OR inline JSON.
|
|
1149
|
+
// - text/json output: wrap the runner with capture + validate + retry
|
|
1150
|
+
// (json-schema-output.js); prints only the validated JSON.
|
|
1151
|
+
// - stream-json output: stream events normally, then emit a terminal
|
|
1152
|
+
// `structured_result` event (schema_digest + valid + value/errors) as
|
|
1153
|
+
// the last NDJSON line, so the two protocols are now compatible.
|
|
1088
1154
|
if (options.jsonSchema) {
|
|
1155
|
+
const jso = await import("../lib/json-schema-output.js");
|
|
1089
1156
|
if (options.outputFormat === "stream-json") {
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1157
|
+
try {
|
|
1158
|
+
const schema = jso.loadSchemaFile(
|
|
1159
|
+
jso._deps.fs,
|
|
1160
|
+
options.jsonSchema,
|
|
1161
|
+
);
|
|
1162
|
+
const instruction = jso.buildSchemaInstruction(schema);
|
|
1163
|
+
const outcome = await runAgentHeadless({
|
|
1164
|
+
...headlessOptions,
|
|
1165
|
+
appendSystemPrompt: [
|
|
1166
|
+
headlessOptions.appendSystemPrompt,
|
|
1167
|
+
instruction,
|
|
1168
|
+
]
|
|
1169
|
+
.filter(Boolean)
|
|
1170
|
+
.join("\n\n"),
|
|
1171
|
+
});
|
|
1172
|
+
// The normal stream (init … result) has already been written to
|
|
1173
|
+
// stdout by the runner. Append the structured verdict as the final
|
|
1174
|
+
// NDJSON line, built from the run's final text.
|
|
1175
|
+
const parsed = jso.extractJsonPayload(
|
|
1176
|
+
String(outcome?.result ?? ""),
|
|
1177
|
+
);
|
|
1178
|
+
const evt = jso.buildStructuredResult(
|
|
1179
|
+
schema,
|
|
1180
|
+
parsed.ok ? parsed.value : null,
|
|
1181
|
+
);
|
|
1182
|
+
process.stdout.write(JSON.stringify(evt) + "\n");
|
|
1183
|
+
await _finishWorktree();
|
|
1184
|
+
process.exit(evt.valid ? 0 : 1);
|
|
1185
|
+
} catch (err) {
|
|
1186
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
1187
|
+
await _finishWorktree();
|
|
1188
|
+
process.exit(1);
|
|
1189
|
+
}
|
|
1094
1190
|
}
|
|
1095
1191
|
try {
|
|
1096
|
-
const
|
|
1097
|
-
await import("../lib/json-schema-output.js");
|
|
1098
|
-
const code = await runJsonSchemaConstrained({
|
|
1192
|
+
const code = await jso.runJsonSchemaConstrained({
|
|
1099
1193
|
schemaFile: options.jsonSchema,
|
|
1100
1194
|
baseOptions: headlessOptions,
|
|
1101
1195
|
runHeadless: runAgentHeadless,
|