@tea-agent/loop-agent 0.40.0-next.9 → 0.41.0-beta.1
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/AGENTS.md +1 -1
- package/CHANGELOG.md +130 -3
- package/README.md +2 -2
- package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
- package/dist/adapters/context-transfer/pi-session.js +61 -0
- package/dist/application/dag/generate-task-dag.js +6 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +21 -5
- package/dist/cli/program.js +61 -4
- package/dist/commands/client-recovery.js +8 -36
- package/dist/commands/dag-artifact.js +284 -0
- package/dist/commands/dag-context.js +184 -0
- package/dist/commands/dag-rerun.js +206 -1
- package/dist/commands/init-model-catalog.js +22 -0
- package/dist/commands/task-advance.js +23 -1
- package/dist/commands/task-source-prepare.js +5 -0
- package/dist/commands/task-status.js +1 -0
- package/dist/executors/dag-pi-executor.js +39 -12
- package/dist/executors/pi-sdk-executor.js +36 -5
- package/dist/executors/shell-executor.js +64 -17
- package/dist/governance/checks.js +1 -0
- package/dist/{worker → infrastructure}/console/app-data.js +4 -0
- package/dist/infrastructure/console/artifact-revision-store.js +430 -0
- package/dist/infrastructure/console/context-export-store.js +160 -0
- package/dist/infrastructure/console/dir-lock.js +132 -0
- package/dist/{worker → infrastructure}/console/operation-store.js +28 -0
- package/dist/infrastructure/harness/artifact-store.js +10 -1
- package/dist/infrastructure/harness/atomic-write.js +12 -2
- package/dist/shared/context-transfer/artifact-revision.js +172 -0
- package/dist/shared/context-transfer.js +418 -0
- package/dist/shared/operator/capabilities.js +23 -1
- package/dist/shared/operator/safe-run-summary.js +1 -0
- package/dist/shared/path-safety.js +93 -0
- package/dist/shared/pi-context-pressure/checkpoint.js +116 -0
- package/dist/shared/pi-context-pressure/compaction-policy.js +151 -0
- package/dist/shared/pi-context-pressure/env.js +58 -0
- package/dist/shared/pi-context-pressure/extension.js +100 -0
- package/dist/shared/pi-context-pressure/index.js +7 -0
- package/dist/shared/pi-context-pressure/overflow.js +252 -0
- package/dist/shared/pi-context-pressure/sift-bridge.js +386 -0
- package/dist/shared/pi-context-pressure/telemetry.js +51 -0
- package/dist/shared/preview.js +28 -4
- package/dist/task/config-types.js +2 -1
- package/dist/task/task-demand-routing.js +3 -0
- package/dist/worker/console/chat/assistant-content.js +7 -0
- package/dist/worker/console/chat/chat-event-store.js +84 -13
- package/dist/worker/console/chat/goal-round-driver.js +72 -0
- package/dist/worker/console/chat/goals.js +300 -0
- package/dist/worker/console/chat/pi-runtime.js +285 -8
- package/dist/worker/console/chat/provider-error.js +2 -1
- package/dist/worker/console/chat/repo-browser.js +94 -12
- package/dist/worker/console/chat/resource-preferences-store.js +1 -1
- package/dist/worker/console/chat/routes.js +487 -30
- package/dist/worker/console/chat/session-catalog.js +48 -0
- package/dist/worker/console/chat/session-stats.js +96 -0
- package/dist/worker/console/chat/session-store.js +28 -19
- package/dist/worker/console/chat/shortcuts.js +7 -0
- package/dist/worker/console/chat/sift-bridge.js +1 -0
- package/dist/worker/console/chat/turn-process.js +32 -3
- package/dist/worker/console/chat/user-questions.js +1 -1
- package/dist/worker/console/console-update-runtime.js +1 -1
- package/dist/worker/console/context-transfer-diagnostics.js +198 -0
- package/dist/worker/console/dag-confirmation.js +1 -1
- package/dist/worker/console/dag-execution-receipt.js +21 -3
- package/dist/worker/console/doctor.js +1 -1
- package/dist/worker/console/draft-store.js +1 -1
- package/dist/worker/console/human-gate-token.js +1 -1
- package/dist/worker/console/index.js +3 -3
- package/dist/worker/console/interview/assessment.js +1 -1
- package/dist/worker/console/interview/session.js +1 -1
- package/dist/worker/console/operation-runner.js +45 -4
- package/dist/worker/console/operation-sse.js +1 -1
- package/dist/worker/console/operation-wait.js +1 -1
- package/dist/worker/console/operator-actions.js +124 -8
- package/dist/worker/console/operator-selection.js +6 -2
- package/dist/worker/console/operator-surface-health.js +1 -1
- package/dist/worker/console/operator-user-error.js +169 -0
- package/dist/worker/console/pi-plugins.catalog.json +73658 -0
- package/dist/worker/console/pi-plugins.js +179 -0
- package/dist/worker/console/routes.js +864 -13
- package/dist/worker/console/security.js +35 -0
- package/dist/worker/console/server.js +177 -44
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bs-CDAXM.js → abnfDiagram-N423BO3Z-DSVcg1YO.js} +1 -1
- package/dist/worker/console/static/assets/{arc-CAkA3We3.js → arc-BtXhIqui.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DEmI_zqr.js → architectureDiagram-T3A2C74G-B-nh-ilJ.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-uDHANebE.js → blockDiagram-VBNYF7ZC-BIUSVdZI.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Bv97b4_B.js → c4Diagram-5PPSVZJV-Cmw3L4qM.js} +1 -1
- package/dist/worker/console/static/assets/channel-BhZFNefx.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-shmQKsFz.js → chunk-2GRJ4B5K-CGpMMjGm.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-BAraNvcN.js → chunk-2Q5K7J3B-BNBkEpEJ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BirhxUop.js → chunk-5RXB4S5H-ubln3eaU.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C9LgUJpR.js → chunk-5VM5RSS4-_XcBTt1i.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DrU-s-t7.js → chunk-6Q2QTUOP-Ln30Pgrh.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Tx-H_FA2.js → chunk-GF5L2VYU-ESH3lhSx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BjLtNFrR.js → chunk-JWPE2WC7-DAJt-qXB.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-UNLVwSsy.js → chunk-KBJHAD2P-Cx10LIcH.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CBEUX8XD.js → chunk-RYQCIY6F-CENeOxoJ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-WTL7IZOq.js → chunk-XXDRQBXY-DXqK-D38.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Sbv-NJni.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Sbv-NJni.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CkHG6W3s.js → cose-bilkent-JH36ORCC-CE4dmvGY.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BRzFqTaL.js → cynefin-VYW2F7L2-CRacN1zK.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-2_CFp_i7.js → cynefinDiagram-MW4NZA55-Cneukjrt.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BJtsmdkE.js → dagre-VZM6K2ZE-DPFboRoL.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DNEFFo6w.js → diagram-7IWD3JNH-DIAQgcOP.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B_IWV7eo.js → diagram-B4RE2ZJO-VrYMKxuy.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DvvRx2dz.js → diagram-LBJQPF4R-CLHvBghn.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-CsibsWLg.js → diagram-Q27KOJAE-DVCWBif8.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-les_djBQ.js → diagram-UB23O5K3-BTMmbBzJ.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BEm2n7qJ.js → ebnfDiagram-BXEA7PRR-BUZsKJ56.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BI7vHHVm.js → erDiagram-JOGREHBK-BsBZ5NXC.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CUFCERrB.js → flowDiagram-UKHOOZJN-D7fH_kDa.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-wxyLiGWt.js → ganttDiagram-PKOTCBZU-Bza5OohY.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bt897K8Z.js → gitGraphDiagram-DS77QQ5N-DpkoGR_Y.js} +1 -1
- package/dist/worker/console/static/assets/index-Ius3vxhf.css +1 -0
- package/dist/worker/console/static/assets/index-yASzcbBr.js +451 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DYNx_IPv.js → infoDiagram-6WML65LV-BYm3g6LT.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CzpLNEu6.js → ishikawaDiagram-WSZJBQD7-C2RS_H4v.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-97f9owNU.js → journeyDiagram-NVQOT4AX-B4slKpCo.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BxM9hyBn.js → kanban-definition-27J2QSJJ-B7orRNpI.js} +1 -1
- package/dist/worker/console/static/assets/{linear-ByuxcHvp.js → linear-D1BeBxLQ.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-DwPGvpCv.js → mermaid.core-0TtMP8k4.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DCoqG9RT.js → mindmap-definition-FAOFIHXS-XL5EbCtT.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BLSBmBMx.js → pegDiagram-VL7TDLO6-CwF7uas5.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B0t_0oJW.js → pieDiagram-7S7Q4E2Y-A_Jo-72f.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-sTzDk7Nt.js → quadrantDiagram-CIZ2JOQS-CxOa4gKL.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-B3jRdATC.js → railroadDiagram-AXF67PYL-DoAoswIH.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D-cTtXEN.js → requirementDiagram-LRYGKXZP-C1Lc1MlD.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B25igJRE.js → sankeyDiagram-W5VNT64P-DFXOkq0U.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-jW_lM1_D.js → sequenceDiagram-SI44F4Z6-B0ufUCuW.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CrzTgsHE.js → sizeCapture-X5ZJPWSS-iPbW55g3.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-B01MvlqE.js → stateDiagram-OKZ733FA-CHiXzEKP.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BtMgzo_H.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-YcF12FiU.js → swimlanes-SLNWSIFB-LyFs7TpX.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cb-OVd9Y.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ClnRcg2s.js → timeline-definition-Z64GVDOM-tNkJnb6z.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-B6gr_z-q.js → vennDiagram-T6HMQDX7-DFm7Oo8o.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CvSgguGH.js → wardleyDiagram-T6FBY63Y-tpblTp9i.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-23OtSZCb.js → xychartDiagram-ELKLHX3M-BCDLWR_D.js} +1 -1
- package/dist/worker/console/static/index.html +7 -2
- package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +95 -25
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +35 -1
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +3 -1
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +26 -19
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +0 -11
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +14 -1
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +34 -4
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
- package/dist/worker/console/static-src/operator-chat/workspace-reorder.js +18 -0
- package/dist/worker/console/static-src/shell/console-update-reload.js +25 -0
- package/dist/worker/console/static-src/shell/useWorkspaces.js +150 -34
- package/dist/worker/console/static-src/shell/workspace-route.js +3 -1
- package/dist/worker/console/workspace-context.js +56 -5
- package/dist/worker/console/workspace-registry.js +335 -62
- package/dist/worker/continuation/worker-continuation.js +278 -0
- package/dist/worker/observe/health.js +1 -1
- package/dist/worker/observe/node-transparency.js +572 -0
- package/dist/worker/observe/routes.js +51 -1
- package/dist/worker/observe/static/api.js +69 -5
- package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
- package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
- package/dist/worker/observe/static/inspect-bootstrap.d.ts +8 -0
- package/dist/worker/observe/static/inspect-bootstrap.js +57 -0
- package/dist/worker/observe/static/inspect-workspace.js +54 -10
- package/dist/worker/observe/static/operator-chrome.css +23 -4
- package/dist/worker/observe/static/operator-chrome.js +7 -2
- package/dist/worker/observe/static/state.js +3 -1
- package/dist/worker/observe/static/styles.css +1108 -135
- package/dist/worker/observe/static/views/dag-inspector.js +1257 -59
- package/dist/worker/observe/static/views/dags.js +7 -5
- package/dist/worker/observe/static/views/session-timeline.js +210 -10
- package/dist/worker/outcomes/adapters.js +19 -0
- package/dist/worker/outcomes/types.js +1 -0
- package/dist/worker/pool/attempt-lease.js +97 -66
- package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
- package/dist/worker/pool/reconcile.js +46 -1
- package/dist/worker/pool/run-store.js +2 -0
- package/dist/worker/pool/state-projection.js +2 -0
- package/dist/worker/task-spec/workflow-routing.js +8 -3
- package/dist/workflows/dag/artifact-bindings.js +149 -0
- package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +162 -8
- package/dist/workflows/dag/backend-test-pytest-collection.js +70 -2
- package/dist/workflows/dag/backend-test-result-contract.js +4 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +339 -53
- package/dist/workflows/dag/backend-test-writer-completeness.js +11 -0
- package/dist/workflows/dag/budget-enforcement.js +10 -0
- package/dist/workflows/dag/context-receipt.js +305 -0
- package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
- package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
- package/dist/workflows/dag/context-transfer/renderers.js +93 -0
- package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
- package/dist/workflows/dag/init-hybrid.js +151 -4
- package/dist/workflows/dag/node-execution.js +112 -27
- package/dist/workflows/dag/path-safety.js +1 -0
- package/dist/workflows/dag/prompt.js +29 -4
- package/dist/workflows/dag/rerun-plan.js +555 -1
- package/dist/workflows/dag/rerun-run.js +601 -15
- package/dist/workflows/dag/runner.js +20 -1
- package/dist/workflows/dag/skill-snapshot.js +17 -0
- package/dist/workflows/dag/types.js +183 -7
- package/docs/architecture/runtime-boundaries.md +8 -7
- package/docs/architecture/worker-and-feature.md +1 -1
- package/docs/templates/agent-dag.schema.json +32 -0
- package/docs/templates/backend-test-dag.json +6 -5
- package/docs/templates/init-managed-agents.md +1 -1
- package/docs/templates/product-line/task.yaml +1 -1
- package/package.json +5 -2
- package/skills/loop-agent/references/command-reference.md +21 -1
- package/dist/worker/console/static/assets/channel-amxUpk7o.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-bejwBVIz.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-bejwBVIz.js +0 -1
- package/dist/worker/console/static/assets/index-BHhUOTri.css +0 -1
- package/dist/worker/console/static/assets/index-DqJbO3-p.js +0 -407
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BvFKEtm2.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DIB2eVg-.js +0 -8
- /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdirSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseBoundedNumber, parseEnabledFlag } from "./env.js";
|
|
5
|
+
import { emptyContextPressureCounters, incrementCounter, } from "./telemetry.js";
|
|
6
|
+
export const SIFT_POLICY_VERSION = "sift-v1-query-undefined";
|
|
7
|
+
export const SIFT_RETRIEVE_TOOL_NAME = "sift_retrieve";
|
|
8
|
+
export const SIFT_STASH_KEY_PATTERN = /^[0-9a-f]{24}$/;
|
|
9
|
+
export const SIFT_RETRIEVE_DEFAULT_BYTES = 24 * 1024;
|
|
10
|
+
export const SIFT_RETRIEVE_MAX_BYTES = 48 * 1024;
|
|
11
|
+
export const DEFAULT_MEMO_MAX_ENTRIES = 128;
|
|
12
|
+
export const DEFAULT_MEMO_MAX_BYTES = 32 * 1024 * 1024;
|
|
13
|
+
export const DEFAULT_STASH_RETENTION_DAYS = 14;
|
|
14
|
+
class BoundedMemo {
|
|
15
|
+
maxEntries;
|
|
16
|
+
maxBytes;
|
|
17
|
+
entries = new Map();
|
|
18
|
+
bytes = 0;
|
|
19
|
+
constructor(maxEntries, maxBytes) {
|
|
20
|
+
this.maxEntries = maxEntries;
|
|
21
|
+
this.maxBytes = maxBytes;
|
|
22
|
+
}
|
|
23
|
+
get(key) {
|
|
24
|
+
const hit = this.entries.get(key);
|
|
25
|
+
if (!hit)
|
|
26
|
+
return undefined;
|
|
27
|
+
this.entries.delete(key);
|
|
28
|
+
this.entries.set(key, hit);
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(hit.json);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
set(key, value) {
|
|
37
|
+
const json = JSON.stringify(value);
|
|
38
|
+
const size = Buffer.byteLength(json);
|
|
39
|
+
if (size > this.maxBytes)
|
|
40
|
+
return;
|
|
41
|
+
this.delete(key);
|
|
42
|
+
while (this.entries.size >= this.maxEntries ||
|
|
43
|
+
this.bytes + size > this.maxBytes) {
|
|
44
|
+
const oldest = this.entries.keys().next().value;
|
|
45
|
+
if (oldest === undefined)
|
|
46
|
+
break;
|
|
47
|
+
this.delete(oldest);
|
|
48
|
+
}
|
|
49
|
+
this.entries.set(key, { json, bytes: size });
|
|
50
|
+
this.bytes += size;
|
|
51
|
+
}
|
|
52
|
+
delete(key) {
|
|
53
|
+
const current = this.entries.get(key);
|
|
54
|
+
if (!current)
|
|
55
|
+
return;
|
|
56
|
+
this.entries.delete(key);
|
|
57
|
+
this.bytes -= current.bytes;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export function parseSiftBridgeConfig(input) {
|
|
61
|
+
const env = input.env ?? process.env;
|
|
62
|
+
const override = env.HARNESS_CONSOLE_SIFT_STASH_DIR?.trim();
|
|
63
|
+
const root = override || input.defaultStashRoot;
|
|
64
|
+
const stashDir = path.join(root, input.sessionId);
|
|
65
|
+
const entries = parseBoundedNumber(env.HARNESS_CONSOLE_SIFT_MEMO_MAX_ENTRIES, DEFAULT_MEMO_MAX_ENTRIES, 8, 4096);
|
|
66
|
+
const bytes = parseBoundedNumber(env.HARNESS_CONSOLE_SIFT_MEMO_MAX_BYTES, DEFAULT_MEMO_MAX_BYTES, 1024, 256 * 1024 * 1024);
|
|
67
|
+
const retention = parseBoundedNumber(env.HARNESS_CONSOLE_SIFT_STASH_RETENTION_DAYS, DEFAULT_STASH_RETENTION_DAYS, 1, 365);
|
|
68
|
+
return {
|
|
69
|
+
sessionId: input.sessionId,
|
|
70
|
+
stashDir,
|
|
71
|
+
memoMaxEntries: entries.value,
|
|
72
|
+
memoMaxBytes: bytes.value,
|
|
73
|
+
retentionDays: retention.value,
|
|
74
|
+
...(input.siftFactory ? { siftFactory: input.siftFactory } : {}),
|
|
75
|
+
...(input.importSift ? { importSift: input.importSift } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export function createSiftBridgeState(config) {
|
|
79
|
+
return {
|
|
80
|
+
sessionId: config.sessionId,
|
|
81
|
+
stashDir: config.stashDir,
|
|
82
|
+
config,
|
|
83
|
+
counters: emptyContextPressureCounters(),
|
|
84
|
+
retrieveBypassIds: new Set(),
|
|
85
|
+
diagnostics: new Set(),
|
|
86
|
+
memo: new BoundedMemo(config.memoMaxEntries, config.memoMaxBytes),
|
|
87
|
+
importFailed: false,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function noteOnce(state, code) {
|
|
91
|
+
if (state.diagnostics.has(code))
|
|
92
|
+
return;
|
|
93
|
+
state.diagnostics.add(code);
|
|
94
|
+
incrementCounter(state.counters, "fail-open");
|
|
95
|
+
}
|
|
96
|
+
function memoKey(payload) {
|
|
97
|
+
return createHash("sha256")
|
|
98
|
+
.update(SIFT_POLICY_VERSION)
|
|
99
|
+
.update("\0")
|
|
100
|
+
.update(JSON.stringify(payload))
|
|
101
|
+
.digest("hex");
|
|
102
|
+
}
|
|
103
|
+
function ensureStashDir(stashDir) {
|
|
104
|
+
mkdirSync(stashDir, { recursive: true, mode: 0o700 });
|
|
105
|
+
}
|
|
106
|
+
async function ensureSift(state) {
|
|
107
|
+
if (state.sift)
|
|
108
|
+
return state.sift;
|
|
109
|
+
if (state.importFailed)
|
|
110
|
+
return undefined;
|
|
111
|
+
try {
|
|
112
|
+
ensureStashDir(state.stashDir);
|
|
113
|
+
if (state.config.siftFactory) {
|
|
114
|
+
state.sift = state.config.siftFactory({ stashDir: state.stashDir });
|
|
115
|
+
return state.sift;
|
|
116
|
+
}
|
|
117
|
+
const importer = state.config.importSift ??
|
|
118
|
+
(async () => (await import("@agent-context/sift")));
|
|
119
|
+
const mod = await importer();
|
|
120
|
+
if (typeof mod.createSift !== "function") {
|
|
121
|
+
state.importFailed = true;
|
|
122
|
+
noteOnce(state, "sift-import-invalid");
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
state.sift = mod.createSift({ stashDir: state.stashDir });
|
|
126
|
+
return state.sift;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
state.importFailed = true;
|
|
130
|
+
noteOnce(state, "sift-import-failed");
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function isRecord(value) {
|
|
135
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
136
|
+
}
|
|
137
|
+
function hasTrailingAnthropicCacheAnchor(payload) {
|
|
138
|
+
if (!isRecord(payload) || !Array.isArray(payload.messages))
|
|
139
|
+
return false;
|
|
140
|
+
const last = payload.messages[payload.messages.length - 1];
|
|
141
|
+
if (!isRecord(last))
|
|
142
|
+
return false;
|
|
143
|
+
const content = last.content;
|
|
144
|
+
if (!Array.isArray(content) || content.length === 0)
|
|
145
|
+
return false;
|
|
146
|
+
const lastBlock = content[content.length - 1];
|
|
147
|
+
return isRecord(lastBlock) && lastBlock.cache_control !== undefined;
|
|
148
|
+
}
|
|
149
|
+
function restoreProtectedToolResults(originalPayload, compressed, protectedIds) {
|
|
150
|
+
if (protectedIds.size === 0)
|
|
151
|
+
return compressed;
|
|
152
|
+
const original = structuredClone(originalPayload);
|
|
153
|
+
const next = structuredClone(compressed);
|
|
154
|
+
restoreInMessages(original, next, protectedIds);
|
|
155
|
+
restoreInResponsesInput(original, next, protectedIds);
|
|
156
|
+
return next;
|
|
157
|
+
}
|
|
158
|
+
function restoreInMessages(original, next, protectedIds) {
|
|
159
|
+
if (!isRecord(original) || !isRecord(next))
|
|
160
|
+
return;
|
|
161
|
+
if (!Array.isArray(original.messages) || !Array.isArray(next.messages))
|
|
162
|
+
return;
|
|
163
|
+
for (let index = 0; index < next.messages.length; index += 1) {
|
|
164
|
+
const origMsg = original.messages[index];
|
|
165
|
+
const nextMsg = next.messages[index];
|
|
166
|
+
if (!isRecord(origMsg) || !isRecord(nextMsg))
|
|
167
|
+
continue;
|
|
168
|
+
const origId = typeof origMsg.tool_call_id === "string"
|
|
169
|
+
? origMsg.tool_call_id
|
|
170
|
+
: undefined;
|
|
171
|
+
if (origId && protectedIds.has(origId)) {
|
|
172
|
+
next.messages[index] = origMsg;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (Array.isArray(origMsg.content) && Array.isArray(nextMsg.content)) {
|
|
176
|
+
for (let blockIndex = 0; blockIndex < nextMsg.content.length; blockIndex += 1) {
|
|
177
|
+
const origBlock = origMsg.content[blockIndex];
|
|
178
|
+
if (!isRecord(origBlock))
|
|
179
|
+
continue;
|
|
180
|
+
const toolUseId = typeof origBlock.tool_use_id === "string"
|
|
181
|
+
? origBlock.tool_use_id
|
|
182
|
+
: undefined;
|
|
183
|
+
if (toolUseId && protectedIds.has(toolUseId)) {
|
|
184
|
+
nextMsg.content[blockIndex] = origBlock;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function restoreInResponsesInput(original, next, protectedIds) {
|
|
191
|
+
if (!isRecord(original) || !isRecord(next))
|
|
192
|
+
return;
|
|
193
|
+
if (!Array.isArray(original.input) || !Array.isArray(next.input))
|
|
194
|
+
return;
|
|
195
|
+
for (let index = 0; index < next.input.length; index += 1) {
|
|
196
|
+
const origItem = original.input[index];
|
|
197
|
+
if (!isRecord(origItem))
|
|
198
|
+
continue;
|
|
199
|
+
const callId = typeof origItem.call_id === "string" ? origItem.call_id : undefined;
|
|
200
|
+
if (callId && protectedIds.has(callId)) {
|
|
201
|
+
next.input[index] = origItem;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export async function compressProviderPayload(state, payload) {
|
|
206
|
+
if (payload === undefined)
|
|
207
|
+
return undefined;
|
|
208
|
+
if (!isRecord(payload)) {
|
|
209
|
+
noteOnce(state, "unknown-format");
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
const key = memoKey(payload);
|
|
213
|
+
const cached = state.memo.get(key);
|
|
214
|
+
if (cached !== undefined)
|
|
215
|
+
return cached;
|
|
216
|
+
if (hasTrailingAnthropicCacheAnchor(payload)) {
|
|
217
|
+
const sift = await ensureSift(state);
|
|
218
|
+
if (!sift)
|
|
219
|
+
return undefined;
|
|
220
|
+
try {
|
|
221
|
+
const result = sift.siftRequest(payload, undefined);
|
|
222
|
+
if (result.changed === false &&
|
|
223
|
+
result.blocksExamined === 0 &&
|
|
224
|
+
result.frozenMessages ===
|
|
225
|
+
(Array.isArray(payload.messages)
|
|
226
|
+
? (payload.messages.length)
|
|
227
|
+
: 0)) {
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
if (!result.changed)
|
|
231
|
+
return undefined;
|
|
232
|
+
noteOnce(state, "anthropic-unexpected-change");
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
noteOnce(state, "sift-handler-error");
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const sift = await ensureSift(state);
|
|
241
|
+
if (!sift)
|
|
242
|
+
return undefined;
|
|
243
|
+
try {
|
|
244
|
+
const result = sift.siftRequest(payload, undefined);
|
|
245
|
+
if (!result || result.changed !== true)
|
|
246
|
+
return undefined;
|
|
247
|
+
const restored = restoreProtectedToolResults(payload, result.body, state.retrieveBypassIds);
|
|
248
|
+
state.counters.tokensSavedEstimate += Math.max(0, result.tokensSaved ?? 0);
|
|
249
|
+
state.counters.blocksCompressed += Math.max(0, result.blocksCompressed ?? 0);
|
|
250
|
+
incrementCounter(state.counters, "wire-compression");
|
|
251
|
+
state.memo.set(key, restored);
|
|
252
|
+
return restored;
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
noteOnce(state, "sift-handler-error");
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
export function clearRetrieveBypass(state) {
|
|
260
|
+
state.retrieveBypassIds.clear();
|
|
261
|
+
}
|
|
262
|
+
export function sliceUtf8Range(text, offset, maxBytes) {
|
|
263
|
+
const totalBytes = Buffer.byteLength(text, "utf8");
|
|
264
|
+
const start = Math.max(0, Math.floor(offset));
|
|
265
|
+
const limit = Math.min(SIFT_RETRIEVE_MAX_BYTES, Math.max(1, Math.floor(maxBytes)));
|
|
266
|
+
if (start >= totalBytes) {
|
|
267
|
+
return { slice: "", nextOffset: null, totalBytes, complete: true };
|
|
268
|
+
}
|
|
269
|
+
const buffer = Buffer.from(text, "utf8");
|
|
270
|
+
let end = Math.min(totalBytes, start + limit);
|
|
271
|
+
while (end > start && (buffer[end] & 0b1100_0000) === 0b1000_0000) {
|
|
272
|
+
end -= 1;
|
|
273
|
+
}
|
|
274
|
+
const slice = buffer.subarray(start, end).toString("utf8");
|
|
275
|
+
const complete = end >= totalBytes;
|
|
276
|
+
return {
|
|
277
|
+
slice,
|
|
278
|
+
nextOffset: complete ? null : end,
|
|
279
|
+
totalBytes,
|
|
280
|
+
complete,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
export async function executeSiftRetrieve(state, toolCallId, params) {
|
|
284
|
+
const key = typeof params.key === "string" ? params.key.trim() : "";
|
|
285
|
+
if (!SIFT_STASH_KEY_PATTERN.test(key)) {
|
|
286
|
+
return {
|
|
287
|
+
content: [
|
|
288
|
+
{
|
|
289
|
+
type: "text",
|
|
290
|
+
text: JSON.stringify({
|
|
291
|
+
error: "STASH_NOT_FOUND_OR_EXPIRED",
|
|
292
|
+
complete: true,
|
|
293
|
+
}),
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const sift = await ensureSift(state);
|
|
299
|
+
const original = sift?.retrieve(key) ?? null;
|
|
300
|
+
if (original === null) {
|
|
301
|
+
return {
|
|
302
|
+
content: [
|
|
303
|
+
{
|
|
304
|
+
type: "text",
|
|
305
|
+
text: JSON.stringify({
|
|
306
|
+
error: "STASH_NOT_FOUND_OR_EXPIRED",
|
|
307
|
+
complete: true,
|
|
308
|
+
}),
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const offset = typeof params.offset === "number" && Number.isFinite(params.offset)
|
|
314
|
+
? Math.max(0, Math.floor(params.offset))
|
|
315
|
+
: 0;
|
|
316
|
+
const maxBytes = typeof params.maxBytes === "number" && Number.isFinite(params.maxBytes)
|
|
317
|
+
? params.maxBytes
|
|
318
|
+
: SIFT_RETRIEVE_DEFAULT_BYTES;
|
|
319
|
+
const page = sliceUtf8Range(original, offset, maxBytes);
|
|
320
|
+
if (toolCallId)
|
|
321
|
+
state.retrieveBypassIds.add(toolCallId);
|
|
322
|
+
return {
|
|
323
|
+
content: [
|
|
324
|
+
{
|
|
325
|
+
type: "text",
|
|
326
|
+
text: JSON.stringify({
|
|
327
|
+
key,
|
|
328
|
+
offset,
|
|
329
|
+
maxBytes: Math.min(SIFT_RETRIEVE_MAX_BYTES, Math.max(1, Math.floor(maxBytes))),
|
|
330
|
+
totalBytes: page.totalBytes,
|
|
331
|
+
nextOffset: page.nextOffset,
|
|
332
|
+
complete: page.complete,
|
|
333
|
+
text: page.slice,
|
|
334
|
+
}),
|
|
335
|
+
},
|
|
336
|
+
],
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
export function housekeepSiftStash(input) {
|
|
340
|
+
let removed = 0;
|
|
341
|
+
try {
|
|
342
|
+
const entries = readdirSync(input.root, { withFileTypes: true });
|
|
343
|
+
const cutoff = (input.now ?? Date.now()) - input.retentionDays * 24 * 60 * 60 * 1000;
|
|
344
|
+
for (const entry of entries) {
|
|
345
|
+
if (!entry.isDirectory())
|
|
346
|
+
continue;
|
|
347
|
+
if (input.activeSessionId && entry.name === input.activeSessionId)
|
|
348
|
+
continue;
|
|
349
|
+
const full = path.join(input.root, entry.name);
|
|
350
|
+
let mtime = 0;
|
|
351
|
+
try {
|
|
352
|
+
mtime = statSync(full).mtimeMs;
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if (mtime <= cutoff) {
|
|
358
|
+
rmSync(full, { recursive: true, force: true });
|
|
359
|
+
removed += 1;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return { removed, failed: false };
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
return { removed, failed: true };
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
export function createSiftRetrieveToolDefinition(state) {
|
|
369
|
+
return {
|
|
370
|
+
name: SIFT_RETRIEVE_TOOL_NAME,
|
|
371
|
+
label: "Sift Retrieve",
|
|
372
|
+
description: "Retrieve a previously compressed tool-output page by 24-hex stash key. Use offset/maxBytes to page; default 24KiB, max 48KiB.",
|
|
373
|
+
parameters: {
|
|
374
|
+
type: "object",
|
|
375
|
+
additionalProperties: false,
|
|
376
|
+
properties: {
|
|
377
|
+
key: { type: "string", description: "24-hex stash key" },
|
|
378
|
+
offset: { type: "number", description: "UTF-8 byte offset" },
|
|
379
|
+
maxBytes: { type: "number", description: "Page size in bytes" },
|
|
380
|
+
},
|
|
381
|
+
required: ["key"],
|
|
382
|
+
},
|
|
383
|
+
execute: (toolCallId, params) => executeSiftRetrieve(state, toolCallId, params),
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
export { parseEnabledFlag };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export function emptyContextPressureCounters() {
|
|
2
|
+
return {
|
|
3
|
+
prePromptThreshold: 0,
|
|
4
|
+
overflow: 0,
|
|
5
|
+
manual: 0,
|
|
6
|
+
wireCompression: 0,
|
|
7
|
+
failOpen: 0,
|
|
8
|
+
usageUnavailable: 0,
|
|
9
|
+
l0CompactFailed: 0,
|
|
10
|
+
tokensSavedEstimate: 0,
|
|
11
|
+
blocksCompressed: 0,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function incrementCounter(counters, reason, amount = 1) {
|
|
15
|
+
switch (reason) {
|
|
16
|
+
case "pre-prompt-threshold":
|
|
17
|
+
counters.prePromptThreshold += amount;
|
|
18
|
+
break;
|
|
19
|
+
case "overflow":
|
|
20
|
+
counters.overflow += amount;
|
|
21
|
+
break;
|
|
22
|
+
case "manual":
|
|
23
|
+
counters.manual += amount;
|
|
24
|
+
break;
|
|
25
|
+
case "wire-compression":
|
|
26
|
+
counters.wireCompression += amount;
|
|
27
|
+
break;
|
|
28
|
+
case "fail-open":
|
|
29
|
+
counters.failOpen += amount;
|
|
30
|
+
break;
|
|
31
|
+
case "usage-unavailable":
|
|
32
|
+
counters.usageUnavailable += amount;
|
|
33
|
+
break;
|
|
34
|
+
case "l0-compact-failed":
|
|
35
|
+
counters.l0CompactFailed += amount;
|
|
36
|
+
break;
|
|
37
|
+
default:
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function projectSafeTelemetry(counters) {
|
|
42
|
+
return {
|
|
43
|
+
prePromptThreshold: counters.prePromptThreshold,
|
|
44
|
+
overflow: counters.overflow,
|
|
45
|
+
manual: counters.manual,
|
|
46
|
+
wireCompression: counters.wireCompression,
|
|
47
|
+
failOpen: counters.failOpen,
|
|
48
|
+
tokensSavedEstimate: counters.tokensSavedEstimate,
|
|
49
|
+
blocksCompressed: counters.blocksCompressed,
|
|
50
|
+
};
|
|
51
|
+
}
|
package/dist/shared/preview.js
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
const ENCODER = new TextEncoder();
|
|
2
2
|
const DECODER = new TextDecoder();
|
|
3
|
+
export function redactSecretsWithFindings(text) {
|
|
4
|
+
let findingCount = 0;
|
|
5
|
+
const replaceChanged = (match, replacement) => {
|
|
6
|
+
if (match !== replacement)
|
|
7
|
+
findingCount += 1;
|
|
8
|
+
return replacement;
|
|
9
|
+
};
|
|
10
|
+
let redacted = text.replace(/(["']?(?:api[_-]?key|token|password|secret|authorization)["']?\s*[:=]\s*["']?)((?:Bearer\s+)?[^"'\s,}]+)/gi, (match, prefix) => replaceChanged(match, `${prefix}***`));
|
|
11
|
+
redacted = redacted.replace(/(\b[A-Z][A-Z0-9_]*=)[^\s]+/g, (match, prefix) => replaceChanged(match, `${prefix}***`));
|
|
12
|
+
redacted = redacted.replace(/\bBearer\s+[^\s"']+/gi, (match) => replaceChanged(match, "Bearer ***"));
|
|
13
|
+
// High-confidence provider-style tokens (OpenAI sk-, GitHub ghp_/gho_,
|
|
14
|
+
// Slack xox*, AWS AKIA) are credential-like even without a key=value
|
|
15
|
+
// prefix (exec-plan 2026-08-25 D4: credential-like 默认阻断).
|
|
16
|
+
redacted = redacted.replace(/\b(?:sk-[A-Za-z0-9_-]{16,}|gh[pos]_[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|AKIA[0-9A-Z]{16})\b/g, (match) => replaceChanged(match, "***"));
|
|
17
|
+
return { redacted, findingCount };
|
|
18
|
+
}
|
|
19
|
+
export function redactSensitivePathsWithFindings(text) {
|
|
20
|
+
let findingCount = 0;
|
|
21
|
+
const replaceHome = (match) => {
|
|
22
|
+
if (match !== "<redacted-home>")
|
|
23
|
+
findingCount += 1;
|
|
24
|
+
return "<redacted-home>";
|
|
25
|
+
};
|
|
26
|
+
let redacted = text.replace(/\/(?:Users|home)\/[^/\s"'\\]+/g, replaceHome);
|
|
27
|
+
redacted = redacted.replace(/\b[A-Za-z]:\\Users\\[^\\/\s"']+/gi, replaceHome);
|
|
28
|
+
return { redacted, findingCount };
|
|
29
|
+
}
|
|
3
30
|
export function redactSecrets(text) {
|
|
4
|
-
return text
|
|
5
|
-
.replace(/\bBearer\s+[^\s"']+/gi, "Bearer ***")
|
|
6
|
-
.replace(/(\b[A-Z][A-Z0-9_]*=)[^\s]+/g, "$1***")
|
|
7
|
-
.replace(/(["']?(?:api[_-]?key|token|password|secret|authorization)["']?\s*[:=]\s*["']?)([^"'\s,}]+)/gi, "$1***");
|
|
31
|
+
return redactSecretsWithFindings(text).redacted;
|
|
8
32
|
}
|
|
9
33
|
export function truncateUtf8Preview(text, maxBytes = 4096) {
|
|
10
34
|
if (maxBytes <= 0)
|
|
@@ -14,6 +14,7 @@ export const taskKindSchema = z.enum([
|
|
|
14
14
|
"frontend-implementation",
|
|
15
15
|
"frontend-test",
|
|
16
16
|
"backend-test",
|
|
17
|
+
"backend-test-analysis",
|
|
17
18
|
"knowledge-sync",
|
|
18
19
|
"knowledge-graph-bootstrap",
|
|
19
20
|
]);
|
|
@@ -163,7 +164,7 @@ const taskConfigObjectSchema = z.object({
|
|
|
163
164
|
taskId: z.string(),
|
|
164
165
|
title: z.string(),
|
|
165
166
|
sourceFiles: z.array(z.string()),
|
|
166
|
-
/** standard: 本仓库需求实现;feature-study: 参考外部代码特性并在目标仓库落地;frontend-implementation: 前端实现 DAG;backend-test: 后端测试 DAG;knowledge-sync: 最终验证后回写测试知识库 DAG;knowledge-graph-bootstrap: AI 辅助业务知识图谱初始化 */
|
|
167
|
+
/** standard: 本仓库需求实现;feature-study: 参考外部代码特性并在目标仓库落地;frontend-implementation: 前端实现 DAG;frontend-test: 前端浏览器测试 DAG;backend-test-analysis: 只读后端测试分析 DAG;backend-test: 后端测试 DAG;knowledge-sync: 最终验证后回写测试知识库 DAG;knowledge-graph-bootstrap: AI 辅助业务知识图谱初始化 */
|
|
167
168
|
taskKind: taskKindSchema.optional().default("standard"),
|
|
168
169
|
/** true = taskKind 由人类显式选择(含显式 standard);DAG 路由必须尊重,不做语义改写。 */
|
|
169
170
|
taskKindExplicit: z.boolean().optional(),
|
|
@@ -76,6 +76,7 @@ const SPECIAL_TEMPLATE_BY_TASK_KIND = {
|
|
|
76
76
|
"frontend-implementation": "frontend-implementation",
|
|
77
77
|
"frontend-test": "frontend-test-dag",
|
|
78
78
|
"backend-test": "backend-test-dag",
|
|
79
|
+
"backend-test-analysis": "backend-test-analysis-dag",
|
|
79
80
|
"knowledge-sync": "knowledge-sync-dag",
|
|
80
81
|
"knowledge-graph-bootstrap": "knowledge-graph-bootstrap-dag",
|
|
81
82
|
};
|
|
@@ -83,6 +84,7 @@ const TASK_KIND_ROUTING_REASON = {
|
|
|
83
84
|
"frontend-implementation": 'taskKind "frontend-implementation" selects the dedicated frontend DAG template',
|
|
84
85
|
"frontend-test": 'taskKind "frontend-test" selects the dedicated frontend test DAG template',
|
|
85
86
|
"backend-test": 'taskKind "backend-test" selects the dedicated backend test DAG template',
|
|
87
|
+
"backend-test-analysis": 'taskKind "backend-test-analysis" selects the read-only backend test analysis DAG template',
|
|
86
88
|
"knowledge-sync": 'taskKind "knowledge-sync" selects the dedicated knowledge-sync DAG template',
|
|
87
89
|
"knowledge-graph-bootstrap": 'taskKind "knowledge-graph-bootstrap" selects the dedicated knowledge-graph bootstrap DAG template',
|
|
88
90
|
};
|
|
@@ -90,6 +92,7 @@ const SPECIAL_REQUESTED_TEMPLATES = new Set([
|
|
|
90
92
|
"frontend-implementation",
|
|
91
93
|
"frontend-test-dag",
|
|
92
94
|
"backend-test-dag",
|
|
95
|
+
"backend-test-analysis-dag",
|
|
93
96
|
"knowledge-sync-dag",
|
|
94
97
|
"knowledge-graph-bootstrap-dag",
|
|
95
98
|
]);
|
|
@@ -98,6 +98,13 @@ function isFinalAnswerBlock(block) {
|
|
|
98
98
|
}
|
|
99
99
|
export function splitFinalAssistantBlocks(blocks, options = {}) {
|
|
100
100
|
const displayable = getDisplayableAssistantBlocks(blocks, options);
|
|
101
|
+
// A streaming Pi message is cumulative: a later update may append a toolCall
|
|
102
|
+
// after text that is already visible. Keep the whole live message in the
|
|
103
|
+
// chronological process subtree so that update appends the tool in place
|
|
104
|
+
// instead of reparenting the earlier text from answer -> process.
|
|
105
|
+
if (options.isStreaming) {
|
|
106
|
+
return { answerBlocks: [], processBlocks: displayable };
|
|
107
|
+
}
|
|
101
108
|
let lastProcessIndex = -1;
|
|
102
109
|
for (let i = displayable.length - 1; i >= 0; i--) {
|
|
103
110
|
if (!isFinalAnswerBlock(displayable[i])) {
|