@junctionpanel/server 0.1.84 → 0.1.86
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/dist/server/client/daemon-client.d.ts +11 -0
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +29 -0
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +4 -0
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +308 -9
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
- package/dist/server/server/agent/agent-projections.js +6 -0
- package/dist/server/server/agent/agent-projections.js.map +1 -1
- package/dist/server/server/agent/agent-storage.d.ts +5 -5
- package/dist/server/server/agent/harness/context.d.ts.map +1 -1
- package/dist/server/server/agent/harness/context.js +63 -1
- package/dist/server/server/agent/harness/context.js.map +1 -1
- package/dist/server/server/agent/harness/permission-policy.d.ts +10 -0
- package/dist/server/server/agent/harness/permission-policy.d.ts.map +1 -0
- package/dist/server/server/agent/harness/permission-policy.js +86 -0
- package/dist/server/server/agent/harness/permission-policy.js.map +1 -0
- package/dist/server/server/agent/harness/risk-classifier.d.ts +8 -0
- package/dist/server/server/agent/harness/risk-classifier.d.ts.map +1 -0
- package/dist/server/server/agent/harness/risk-classifier.js +73 -0
- package/dist/server/server/agent/harness/risk-classifier.js.map +1 -0
- package/dist/server/server/agent/harness/run-ledger.d.ts +21 -0
- package/dist/server/server/agent/harness/run-ledger.d.ts.map +1 -0
- package/dist/server/server/agent/harness/run-ledger.js +79 -0
- package/dist/server/server/agent/harness/run-ledger.js.map +1 -0
- package/dist/server/server/agent/harness/session-bundle.d.ts +13 -0
- package/dist/server/server/agent/harness/session-bundle.d.ts.map +1 -0
- package/dist/server/server/agent/harness/session-bundle.js +50 -0
- package/dist/server/server/agent/harness/session-bundle.js.map +1 -0
- package/dist/server/server/agent/harness/types.d.ts +86 -2
- package/dist/server/server/agent/harness/types.d.ts.map +1 -1
- package/dist/server/server/session.d.ts +10 -0
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +59 -1
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/websocket-server.d.ts +1 -0
- package/dist/server/server/websocket-server.d.ts.map +1 -1
- package/dist/server/server/websocket-server.js +20 -0
- package/dist/server/server/websocket-server.js.map +1 -1
- package/dist/server/server/workspace-read-state-store.d.ts +15 -0
- package/dist/server/server/workspace-read-state-store.d.ts.map +1 -0
- package/dist/server/server/workspace-read-state-store.js +87 -0
- package/dist/server/server/workspace-read-state-store.js.map +1 -0
- package/dist/server/shared/messages.d.ts +1444 -762
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +144 -0
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/utils/worktree-metadata.d.ts +2 -2
- package/package.json +2 -2
|
@@ -9,6 +9,9 @@ import { buildPermissionRecoveryFingerprint } from "./agent-permission-fingerpri
|
|
|
9
9
|
import { isCodexPlanModeEnabled, normalizeCodexModeId, setCodexPlanModeEnabled, } from "./codex-config.js";
|
|
10
10
|
import { applyHarnessSystemPrompt, buildHarnessContext, compileHarnessSystemPrompt, getHarnessSessionExtra, mergeRuntimeInfoWithHarness, setHarnessSessionExtra, } from "./harness/context.js";
|
|
11
11
|
import { summarizeTurnFromTimeline, updateSessionState } from "./harness/memory.js";
|
|
12
|
+
import { createHarnessPermissionLedgerEntry, resolveHarnessPermissionLedger, } from "./harness/permission-policy.js";
|
|
13
|
+
import { getActiveHarnessRun, touchHarnessRunProgress, transitionHarnessRunLedger, } from "./harness/run-ledger.js";
|
|
14
|
+
import { accumulateHarnessCostState, createHarnessResumeState, ensureHarnessScratchpadDir, touchHarnessResumeState, } from "./harness/session-bundle.js";
|
|
12
15
|
import { JUNCTION_HARNESS_VERSION } from "./harness/types.js";
|
|
13
16
|
import { getPendingPlanReviewFingerprint, hasPendingPlanReview, } from "./pending-plan-review.js";
|
|
14
17
|
import { extractPermissionQuestionAnswersFromResponse, formatPermissionQuestionAnswers, isInteractiveQuestionPermission, } from "../../shared/permission-questions.js";
|
|
@@ -43,6 +46,36 @@ function extractGeminiFallbackPermissionUpdate(response) {
|
|
|
43
46
|
const persistedSummary = typeof fallback.persistedSummary === "string" ? fallback.persistedSummary.trim() : "";
|
|
44
47
|
return persistedSummary.length > 0 ? { kind, persistedSummary } : { kind };
|
|
45
48
|
}
|
|
49
|
+
function inferHarnessWorkerRole(detail) {
|
|
50
|
+
const haystack = `${detail.subAgentType ?? ""} ${detail.description ?? ""}`.toLowerCase();
|
|
51
|
+
if (haystack.includes("explore") || haystack.includes("research")) {
|
|
52
|
+
return "explore";
|
|
53
|
+
}
|
|
54
|
+
if (haystack.includes("plan")) {
|
|
55
|
+
return "plan";
|
|
56
|
+
}
|
|
57
|
+
if (haystack.includes("verify") || haystack.includes("test")) {
|
|
58
|
+
return "verify";
|
|
59
|
+
}
|
|
60
|
+
if (haystack.includes("implement") || haystack.includes("fix")) {
|
|
61
|
+
return "implement";
|
|
62
|
+
}
|
|
63
|
+
return "generic";
|
|
64
|
+
}
|
|
65
|
+
function mapTurnOutcomeToRunLifecycle(outcome) {
|
|
66
|
+
switch (outcome) {
|
|
67
|
+
case "provider_error":
|
|
68
|
+
return "error";
|
|
69
|
+
case "canceled":
|
|
70
|
+
return "interrupted";
|
|
71
|
+
case "completed":
|
|
72
|
+
case "needs_verification":
|
|
73
|
+
return "completed";
|
|
74
|
+
case "blocked_on_permission":
|
|
75
|
+
case "blocked_on_feedback":
|
|
76
|
+
return "waiting_permission";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
46
79
|
function attachPersistenceCwd(handle, cwd) {
|
|
47
80
|
if (!handle) {
|
|
48
81
|
return null;
|
|
@@ -532,6 +565,7 @@ export class AgentManager {
|
|
|
532
565
|
return this.registerSession(session, normalizedConfig, resolvedAgentId, {
|
|
533
566
|
labels: options?.labels,
|
|
534
567
|
parentAgentId: options?.parentAgentId ?? null,
|
|
568
|
+
resumeSource: "new_session",
|
|
535
569
|
});
|
|
536
570
|
}
|
|
537
571
|
// Reconstruct an agent from provider persistence. Callers should explicitly
|
|
@@ -550,7 +584,7 @@ export class AgentManager {
|
|
|
550
584
|
: overrides;
|
|
551
585
|
const client = this.requireClient(handle.provider);
|
|
552
586
|
const session = await client.resumeSession(handle, resumeOverrides);
|
|
553
|
-
return this.registerSession(session, normalizedConfig, resolvedAgentId, options);
|
|
587
|
+
return this.registerSession(session, normalizedConfig, resolvedAgentId, { ...options, resumeSource: "provider_resume" });
|
|
554
588
|
}
|
|
555
589
|
// Hot-reload an active agent session with config overrides while preserving
|
|
556
590
|
// in-memory timeline state.
|
|
@@ -614,6 +648,7 @@ export class AgentManager {
|
|
|
614
648
|
activeTurnStartedAt: preservedActiveTurnStartedAt,
|
|
615
649
|
lastError: preservedLastError,
|
|
616
650
|
attention: preservedAttention,
|
|
651
|
+
resumeSource: handle ? "provider_resume" : "new_session",
|
|
617
652
|
});
|
|
618
653
|
}
|
|
619
654
|
async closeAgent(agentId) {
|
|
@@ -1045,13 +1080,15 @@ export class AgentManager {
|
|
|
1045
1080
|
}
|
|
1046
1081
|
}
|
|
1047
1082
|
await agent.session.respondToPermission(requestId, response);
|
|
1048
|
-
|
|
1049
|
-
this.clearAttentionForUserEngagement(agent);
|
|
1050
|
-
this.recordPermissionResolutionTimelineItem(agent, requestId, response, {
|
|
1083
|
+
this.resolvePendingPermission(agent, requestId, response, {
|
|
1051
1084
|
provider: agent.provider,
|
|
1052
|
-
|
|
1085
|
+
dispatchEvent: false,
|
|
1053
1086
|
dispatchTimeline: true,
|
|
1087
|
+
syncHarness: true,
|
|
1088
|
+
dispatchHarnessEvent: false,
|
|
1089
|
+
transitionRunToStreaming: getActiveHarnessRun(getHarnessSessionExtra(agent.config)?.runLedger) !== null,
|
|
1054
1090
|
});
|
|
1091
|
+
this.clearAttentionForUserEngagement(agent);
|
|
1055
1092
|
// Update currentModeId - the session may have changed mode internally
|
|
1056
1093
|
// (e.g., plan approval changes mode from "plan" to "acceptEdits")
|
|
1057
1094
|
try {
|
|
@@ -1123,6 +1160,9 @@ export class AgentManager {
|
|
|
1123
1160
|
provider: agent.provider,
|
|
1124
1161
|
dispatchEvent: true,
|
|
1125
1162
|
dispatchTimeline: true,
|
|
1163
|
+
syncHarness: true,
|
|
1164
|
+
dispatchHarnessEvent: true,
|
|
1165
|
+
transitionRunToStreaming: false,
|
|
1126
1166
|
});
|
|
1127
1167
|
}
|
|
1128
1168
|
this.emitState(agent);
|
|
@@ -1171,7 +1211,14 @@ export class AgentManager {
|
|
|
1171
1211
|
for (const [requestId, request] of agent.pendingPermissions) {
|
|
1172
1212
|
const fingerprint = buildPermissionRecoveryFingerprint(request);
|
|
1173
1213
|
if (requestIds.has(request.id) || requestFingerprints.has(fingerprint)) {
|
|
1174
|
-
|
|
1214
|
+
this.resolvePendingPermission(agent, requestId, { behavior: "deny", message: "Approval request expired while the daemon was offline." }, {
|
|
1215
|
+
provider: agent.provider,
|
|
1216
|
+
dispatchEvent: false,
|
|
1217
|
+
dispatchTimeline: false,
|
|
1218
|
+
syncHarness: true,
|
|
1219
|
+
dispatchHarnessEvent: false,
|
|
1220
|
+
transitionRunToStreaming: false,
|
|
1221
|
+
});
|
|
1175
1222
|
removed += 1;
|
|
1176
1223
|
}
|
|
1177
1224
|
}
|
|
@@ -1408,6 +1455,18 @@ export class AgentManager {
|
|
|
1408
1455
|
internal: config.internal ?? false,
|
|
1409
1456
|
labels: options?.labels ?? {},
|
|
1410
1457
|
};
|
|
1458
|
+
const registeredAt = (options?.updatedAt ?? options?.createdAt ?? now).toISOString();
|
|
1459
|
+
const existingHarnessExtra = getHarnessSessionExtra(config);
|
|
1460
|
+
setHarnessSessionExtra(config, {
|
|
1461
|
+
scratchpadDir: existingHarnessExtra?.scratchpadDir ?? ensureHarnessScratchpadDir(resolvedAgentId),
|
|
1462
|
+
resumeState: existingHarnessExtra?.resumeState
|
|
1463
|
+
? touchHarnessResumeState(existingHarnessExtra.resumeState, {
|
|
1464
|
+
status: existingHarnessExtra.resumeState.status === "checkpoint_restored" ? "checkpoint_restored" : "live",
|
|
1465
|
+
source: existingHarnessExtra.resumeState.source,
|
|
1466
|
+
timestamp: registeredAt,
|
|
1467
|
+
})
|
|
1468
|
+
: createHarnessResumeState(options?.resumeSource ?? "new_session", registeredAt),
|
|
1469
|
+
});
|
|
1411
1470
|
this.agents.set(resolvedAgentId, managed);
|
|
1412
1471
|
// Initialize previousStatus to track transitions
|
|
1413
1472
|
this.previousStatuses.set(resolvedAgentId, managed.lifecycle);
|
|
@@ -1607,6 +1666,11 @@ export class AgentManager {
|
|
|
1607
1666
|
agent.lastUserMessageAt = new Date();
|
|
1608
1667
|
this.emitState(agent);
|
|
1609
1668
|
}
|
|
1669
|
+
if (!options?.fromHistory &&
|
|
1670
|
+
event.item.type !== "permission_request" &&
|
|
1671
|
+
event.item.type !== "permission_resolution") {
|
|
1672
|
+
this.touchActiveHarnessRunProgress(agent);
|
|
1673
|
+
}
|
|
1610
1674
|
break;
|
|
1611
1675
|
case "turn_completed":
|
|
1612
1676
|
this.pendingUserInterruptAgents.delete(agent.id);
|
|
@@ -1616,7 +1680,20 @@ export class AgentManager {
|
|
|
1616
1680
|
let shouldEmitState = pendingRefresh.changed;
|
|
1617
1681
|
agent.lastUsage = event.usage;
|
|
1618
1682
|
agent.lastError = undefined;
|
|
1619
|
-
|
|
1683
|
+
if (!options?.fromHistory) {
|
|
1684
|
+
this.finalizeHarnessTurn(agent, "completed");
|
|
1685
|
+
const timestamp = new Date().toISOString();
|
|
1686
|
+
const harnessExtra = getHarnessSessionExtra(agent.config);
|
|
1687
|
+
setHarnessSessionExtra(agent.config, {
|
|
1688
|
+
costState: accumulateHarnessCostState(harnessExtra?.costState, event.usage, timestamp),
|
|
1689
|
+
resumeState: touchHarnessResumeState(harnessExtra?.resumeState, {
|
|
1690
|
+
status: "live",
|
|
1691
|
+
source: harnessExtra?.resumeState?.source ?? "snapshot",
|
|
1692
|
+
timestamp,
|
|
1693
|
+
recoveryHint: null,
|
|
1694
|
+
}),
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1620
1697
|
if (!options?.fromHistory) {
|
|
1621
1698
|
const turnSummaryItem = this.buildTurnSummaryTimelineItem(agent, event.usage, event.modelId, agent.activeTurnStartedAt);
|
|
1622
1699
|
if (turnSummaryItem) {
|
|
@@ -1648,12 +1725,24 @@ export class AgentManager {
|
|
|
1648
1725
|
}
|
|
1649
1726
|
agent.activeTurnStartedAt = null;
|
|
1650
1727
|
agent.lastError = event.error;
|
|
1651
|
-
|
|
1728
|
+
if (!options?.fromHistory) {
|
|
1729
|
+
this.finalizeHarnessTurn(agent, "provider_error");
|
|
1730
|
+
this.recordHarnessEvent(agent, {
|
|
1731
|
+
kind: "recovery_hint_emitted",
|
|
1732
|
+
summary: "The provider run failed. Review the error and retry or resume the task.",
|
|
1733
|
+
details: {
|
|
1734
|
+
error: event.error,
|
|
1735
|
+
},
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1652
1738
|
for (const [requestId] of agent.pendingPermissions) {
|
|
1653
1739
|
this.resolvePendingPermission(agent, requestId, { behavior: "deny", message: "Turn failed" }, {
|
|
1654
1740
|
provider: event.provider,
|
|
1655
1741
|
dispatchEvent: !options?.fromHistory,
|
|
1656
1742
|
dispatchTimeline: !options?.fromHistory,
|
|
1743
|
+
syncHarness: !options?.fromHistory,
|
|
1744
|
+
dispatchHarnessEvent: !options?.fromHistory,
|
|
1745
|
+
transitionRunToStreaming: false,
|
|
1657
1746
|
});
|
|
1658
1747
|
}
|
|
1659
1748
|
this.emitState(agent);
|
|
@@ -1671,13 +1760,28 @@ export class AgentManager {
|
|
|
1671
1760
|
}
|
|
1672
1761
|
agent.activeTurnStartedAt = null;
|
|
1673
1762
|
agent.lastError = undefined;
|
|
1674
|
-
|
|
1763
|
+
if (!options?.fromHistory) {
|
|
1764
|
+
this.finalizeHarnessTurn(agent, "canceled");
|
|
1765
|
+
const timestamp = new Date().toISOString();
|
|
1766
|
+
const harnessExtra = getHarnessSessionExtra(agent.config);
|
|
1767
|
+
setHarnessSessionExtra(agent.config, {
|
|
1768
|
+
resumeState: touchHarnessResumeState(harnessExtra?.resumeState, {
|
|
1769
|
+
status: "live",
|
|
1770
|
+
source: harnessExtra?.resumeState?.source ?? "snapshot",
|
|
1771
|
+
timestamp,
|
|
1772
|
+
recoveryHint: "The run was interrupted before completion.",
|
|
1773
|
+
}),
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1675
1776
|
if (!pendingRefresh.hasPendingPermissions) {
|
|
1676
1777
|
for (const requestId of pendingRequestIds) {
|
|
1677
1778
|
this.resolvePendingPermission(agent, requestId, { behavior: "deny", message: "Interrupted" }, {
|
|
1678
1779
|
provider: event.provider,
|
|
1679
1780
|
dispatchEvent: !options?.fromHistory,
|
|
1680
1781
|
dispatchTimeline: !options?.fromHistory,
|
|
1782
|
+
syncHarness: !options?.fromHistory,
|
|
1783
|
+
dispatchHarnessEvent: !options?.fromHistory,
|
|
1784
|
+
transitionRunToStreaming: false,
|
|
1681
1785
|
});
|
|
1682
1786
|
}
|
|
1683
1787
|
}
|
|
@@ -1701,12 +1805,22 @@ export class AgentManager {
|
|
|
1701
1805
|
if (!agent.pendingRun) {
|
|
1702
1806
|
agent.lifecycle = "running";
|
|
1703
1807
|
}
|
|
1808
|
+
if (!options?.fromHistory) {
|
|
1809
|
+
this.transitionHarnessRunState(agent, {
|
|
1810
|
+
lifecycle: "streaming",
|
|
1811
|
+
summary: "Run is streaming.",
|
|
1812
|
+
turnStartSeq: getHarnessSessionExtra(agent.config)?.currentTurn?.turnStartSeq ?? null,
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1704
1815
|
this.emitState(agent);
|
|
1705
1816
|
shouldPersistSnapshot = true;
|
|
1706
1817
|
break;
|
|
1707
1818
|
case "permission_requested":
|
|
1708
1819
|
{
|
|
1709
1820
|
agent.pendingPermissions.set(event.request.id, event.request);
|
|
1821
|
+
if (!options?.fromHistory) {
|
|
1822
|
+
this.recordHarnessPermissionRequest(agent, event.request, true);
|
|
1823
|
+
}
|
|
1710
1824
|
this.recordPermissionRequestTimelineItem(agent, event.request, {
|
|
1711
1825
|
provider: event.provider,
|
|
1712
1826
|
dispatchTimeline: !options?.fromHistory,
|
|
@@ -1738,6 +1852,9 @@ export class AgentManager {
|
|
|
1738
1852
|
provider: event.provider,
|
|
1739
1853
|
dispatchEvent: false,
|
|
1740
1854
|
dispatchTimeline: !options?.fromHistory,
|
|
1855
|
+
syncHarness: !options?.fromHistory,
|
|
1856
|
+
dispatchHarnessEvent: !options?.fromHistory,
|
|
1857
|
+
transitionRunToStreaming: !options?.fromHistory,
|
|
1741
1858
|
});
|
|
1742
1859
|
this.emitState(agent);
|
|
1743
1860
|
shouldPersistSnapshot = true;
|
|
@@ -1864,6 +1981,12 @@ export class AgentManager {
|
|
|
1864
1981
|
}
|
|
1865
1982
|
resolvePendingPermission(agent, requestId, resolution, options) {
|
|
1866
1983
|
const pendingRequest = agent.pendingPermissions.get(requestId) ?? null;
|
|
1984
|
+
if (options.syncHarness) {
|
|
1985
|
+
this.recordHarnessPermissionResolution(agent, requestId, resolution, {
|
|
1986
|
+
dispatchEvent: options.dispatchHarnessEvent ?? false,
|
|
1987
|
+
transitionRunToStreaming: options.transitionRunToStreaming ?? false,
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1867
1990
|
agent.pendingPermissions.delete(requestId);
|
|
1868
1991
|
const row = this.recordPermissionResolutionTimelineItem(agent, requestId, resolution, {
|
|
1869
1992
|
provider: options.provider,
|
|
@@ -2194,6 +2317,117 @@ export class AgentManager {
|
|
|
2194
2317
|
epoch: this.ensureTimelineState(agent).epoch,
|
|
2195
2318
|
});
|
|
2196
2319
|
}
|
|
2320
|
+
transitionHarnessRunState(agent, input) {
|
|
2321
|
+
const timestamp = new Date().toISOString();
|
|
2322
|
+
const current = getHarnessSessionExtra(agent.config);
|
|
2323
|
+
const previousRun = getActiveHarnessRun(current?.runLedger);
|
|
2324
|
+
const { ledger, run } = transitionHarnessRunLedger(current?.runLedger, {
|
|
2325
|
+
lifecycle: input.lifecycle,
|
|
2326
|
+
timestamp,
|
|
2327
|
+
idFactory: this.idFactory,
|
|
2328
|
+
owner: agent.internal ? "background" : "foreground",
|
|
2329
|
+
summary: input.summary,
|
|
2330
|
+
turnStartSeq: input.turnStartSeq,
|
|
2331
|
+
turnEndSeq: input.turnEndSeq,
|
|
2332
|
+
permissionRequestId: input.permissionRequestId,
|
|
2333
|
+
correlationId: agent.persistence?.sessionId ?? agent.runtimeInfo?.sessionId ?? null,
|
|
2334
|
+
modelId: agent.runtimeInfo?.model ?? agent.config.model ?? null,
|
|
2335
|
+
progress: input.lifecycle === "streaming",
|
|
2336
|
+
});
|
|
2337
|
+
setHarnessSessionExtra(agent.config, {
|
|
2338
|
+
runLedger: ledger,
|
|
2339
|
+
});
|
|
2340
|
+
if (input.dispatchEvent !== false &&
|
|
2341
|
+
(previousRun?.lifecycle !== run.lifecycle ||
|
|
2342
|
+
previousRun?.permissionRequestId !== run.permissionRequestId)) {
|
|
2343
|
+
this.recordHarnessEvent(agent, {
|
|
2344
|
+
kind: "run_state_changed",
|
|
2345
|
+
summary: input.summary ?? `Run is now ${run.lifecycle}.`,
|
|
2346
|
+
details: {
|
|
2347
|
+
runId: run.id,
|
|
2348
|
+
lifecycle: run.lifecycle,
|
|
2349
|
+
permissionRequestId: run.permissionRequestId,
|
|
2350
|
+
},
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
touchActiveHarnessRunProgress(agent) {
|
|
2355
|
+
const current = getHarnessSessionExtra(agent.config);
|
|
2356
|
+
const nextLedger = touchHarnessRunProgress(current?.runLedger, new Date().toISOString());
|
|
2357
|
+
if (!nextLedger || nextLedger === current?.runLedger) {
|
|
2358
|
+
return;
|
|
2359
|
+
}
|
|
2360
|
+
setHarnessSessionExtra(agent.config, {
|
|
2361
|
+
runLedger: nextLedger,
|
|
2362
|
+
});
|
|
2363
|
+
}
|
|
2364
|
+
recordHarnessPermissionRequest(agent, request, dispatchEvent) {
|
|
2365
|
+
const timestamp = new Date().toISOString();
|
|
2366
|
+
const current = getHarnessSessionExtra(agent.config);
|
|
2367
|
+
const existing = (current?.permissionLedger ?? []).filter((entry) => entry.requestId !== request.id);
|
|
2368
|
+
const nextEntry = createHarnessPermissionLedgerEntry(request, timestamp);
|
|
2369
|
+
setHarnessSessionExtra(agent.config, {
|
|
2370
|
+
permissionLedger: [nextEntry, ...existing].slice(0, 50),
|
|
2371
|
+
});
|
|
2372
|
+
this.transitionHarnessRunState(agent, {
|
|
2373
|
+
lifecycle: "waiting_permission",
|
|
2374
|
+
summary: `Waiting for permission: ${request.name}.`,
|
|
2375
|
+
permissionRequestId: request.id,
|
|
2376
|
+
dispatchEvent,
|
|
2377
|
+
});
|
|
2378
|
+
if (dispatchEvent) {
|
|
2379
|
+
this.recordHarnessEvent(agent, {
|
|
2380
|
+
kind: "permission_enqueued",
|
|
2381
|
+
summary: `Queued ${nextEntry.risk} permission request for ${request.name}.`,
|
|
2382
|
+
details: {
|
|
2383
|
+
requestId: request.id,
|
|
2384
|
+
risk: nextEntry.risk,
|
|
2385
|
+
fingerprint: nextEntry.fingerprint,
|
|
2386
|
+
kind: request.kind,
|
|
2387
|
+
},
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
recordHarnessPermissionResolution(agent, requestId, resolution, options) {
|
|
2392
|
+
const timestamp = new Date().toISOString();
|
|
2393
|
+
const current = getHarnessSessionExtra(agent.config);
|
|
2394
|
+
const nextLedger = resolveHarnessPermissionLedger(current?.permissionLedger, requestId, resolution, timestamp);
|
|
2395
|
+
const resolvedEntry = nextLedger.find((entry) => entry.requestId === requestId) ?? null;
|
|
2396
|
+
setHarnessSessionExtra(agent.config, {
|
|
2397
|
+
permissionLedger: nextLedger,
|
|
2398
|
+
});
|
|
2399
|
+
if (options.dispatchEvent && resolvedEntry && agent.lifecycle !== "closed") {
|
|
2400
|
+
this.recordHarnessEvent(agent, {
|
|
2401
|
+
kind: "permission_resolved",
|
|
2402
|
+
summary: `Permission ${resolvedEntry.status} for ${resolvedEntry.toolName}.`,
|
|
2403
|
+
details: {
|
|
2404
|
+
requestId,
|
|
2405
|
+
risk: resolvedEntry.risk,
|
|
2406
|
+
status: resolvedEntry.status,
|
|
2407
|
+
},
|
|
2408
|
+
});
|
|
2409
|
+
if (resolvedEntry.recoveryHint && resolvedEntry.status !== "allowed") {
|
|
2410
|
+
this.recordHarnessEvent(agent, {
|
|
2411
|
+
kind: "recovery_hint_emitted",
|
|
2412
|
+
summary: resolvedEntry.recoveryHint,
|
|
2413
|
+
details: {
|
|
2414
|
+
requestId,
|
|
2415
|
+
status: resolvedEntry.status,
|
|
2416
|
+
},
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
if (options.transitionRunToStreaming && agent.lifecycle !== "closed") {
|
|
2421
|
+
this.transitionHarnessRunState(agent, {
|
|
2422
|
+
lifecycle: "streaming",
|
|
2423
|
+
summary: resolvedEntry != null
|
|
2424
|
+
? `Permission ${resolvedEntry.status} for ${resolvedEntry.toolName}.`
|
|
2425
|
+
: "Permission resolved.",
|
|
2426
|
+
permissionRequestId: null,
|
|
2427
|
+
dispatchEvent: false,
|
|
2428
|
+
});
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2197
2431
|
upsertCheckpointSummary(existing, summary) {
|
|
2198
2432
|
return [summary, ...(existing ?? []).filter((entry) => entry.id !== summary.id)].slice(0, 50);
|
|
2199
2433
|
}
|
|
@@ -2219,18 +2453,46 @@ export class AgentManager {
|
|
|
2219
2453
|
provider: agent.provider,
|
|
2220
2454
|
threadId: null,
|
|
2221
2455
|
parentAgentId: agent.id,
|
|
2456
|
+
role: inferHarnessWorkerRole(item.detail),
|
|
2457
|
+
kind: "provider_subagent",
|
|
2222
2458
|
purpose: item.detail.description?.trim() ||
|
|
2223
2459
|
item.detail.subAgentType?.trim() ||
|
|
2224
2460
|
"Sub-agent task",
|
|
2225
2461
|
owner: objective,
|
|
2226
2462
|
status: nextStatus,
|
|
2463
|
+
queueState: nextStatus === "running" ? "active" : "completed",
|
|
2227
2464
|
outputSummary: item.detail.actions[item.detail.actions.length - 1]?.summary ??
|
|
2228
2465
|
item.detail.description ??
|
|
2229
2466
|
existing?.outputSummary ??
|
|
2230
2467
|
null,
|
|
2231
2468
|
changedFiles: existing?.changedFiles ?? [],
|
|
2232
2469
|
verificationState: existing?.verificationState ?? "not_needed",
|
|
2470
|
+
startedAt: existing?.startedAt ?? row.timestamp,
|
|
2233
2471
|
integratedAt: existing?.integratedAt ?? null,
|
|
2472
|
+
completedAt: nextStatus === "completed" || nextStatus === "integrated" || nextStatus === "failed"
|
|
2473
|
+
? timestamp
|
|
2474
|
+
: existing?.completedAt ?? null,
|
|
2475
|
+
sessionId: existing?.sessionId ?? null,
|
|
2476
|
+
transcriptPath: existing?.transcriptPath ?? null,
|
|
2477
|
+
outputPath: existing?.outputPath ?? null,
|
|
2478
|
+
rolloutPath: existing?.rolloutPath ?? null,
|
|
2479
|
+
recentActions: item.detail.actions.map((action) => ({ ...action })),
|
|
2480
|
+
permissionState: existing?.permissionState ?? "none",
|
|
2481
|
+
lastPermissionRequestId: existing?.lastPermissionRequestId ?? null,
|
|
2482
|
+
lastPermissionToolName: existing?.lastPermissionToolName ?? null,
|
|
2483
|
+
error: item.status === "failed"
|
|
2484
|
+
? typeof item.error === "object" && item.error !== null && "message" in item.error
|
|
2485
|
+
? String(item.error.message ?? "Worker failed")
|
|
2486
|
+
: String(item.error)
|
|
2487
|
+
: null,
|
|
2488
|
+
progress: {
|
|
2489
|
+
toolCallCount: item.detail.actions.length,
|
|
2490
|
+
lastActivityAt: row.timestamp,
|
|
2491
|
+
latestSummary: item.detail.actions[item.detail.actions.length - 1]?.summary ??
|
|
2492
|
+
item.detail.description ??
|
|
2493
|
+
existing?.progress?.latestSummary ??
|
|
2494
|
+
null,
|
|
2495
|
+
},
|
|
2234
2496
|
lastUpdatedAt: timestamp,
|
|
2235
2497
|
};
|
|
2236
2498
|
}
|
|
@@ -2304,12 +2566,22 @@ export class AgentManager {
|
|
|
2304
2566
|
const compiledSystemPrompt = compileHarnessSystemPrompt(context);
|
|
2305
2567
|
applyHarnessSystemPrompt(agent.config, compiledSystemPrompt);
|
|
2306
2568
|
const startedAt = new Date().toISOString();
|
|
2569
|
+
const scratchpadDir = previousHarnessExtra?.scratchpadDir ?? ensureHarnessScratchpadDir(agent.id);
|
|
2307
2570
|
setHarnessSessionExtra(agent.config, {
|
|
2308
2571
|
workspaceSynopsis: context.workspaceSynopsis,
|
|
2309
2572
|
trustState: context.trustState,
|
|
2310
2573
|
contextLayers: context.promptLayers.map((layer) => layer.id),
|
|
2311
2574
|
providerInstructionStrategy,
|
|
2312
2575
|
harnessVersion: JUNCTION_HARNESS_VERSION,
|
|
2576
|
+
scratchpadDir,
|
|
2577
|
+
resumeState: previousHarnessExtra?.resumeState
|
|
2578
|
+
? touchHarnessResumeState(previousHarnessExtra.resumeState, {
|
|
2579
|
+
status: "live",
|
|
2580
|
+
source: previousHarnessExtra.resumeState.source,
|
|
2581
|
+
timestamp: startedAt,
|
|
2582
|
+
recoveryHint: null,
|
|
2583
|
+
})
|
|
2584
|
+
: createHarnessResumeState("new_session", startedAt),
|
|
2313
2585
|
currentTurn: {
|
|
2314
2586
|
classification: context.classification,
|
|
2315
2587
|
objective: context.objective,
|
|
@@ -2418,6 +2690,27 @@ export class AgentManager {
|
|
|
2418
2690
|
lastValidatedAt: turnSummary.lastSuccessfulValidation?.timestamp ?? harnessExtra?.lastValidatedAt ?? null,
|
|
2419
2691
|
currentTurn: undefined,
|
|
2420
2692
|
harnessVersion: JUNCTION_HARNESS_VERSION,
|
|
2693
|
+
runLedger: transitionHarnessRunLedger(harnessExtra?.runLedger, {
|
|
2694
|
+
lifecycle: mapTurnOutcomeToRunLifecycle(resolvedOutcome),
|
|
2695
|
+
timestamp,
|
|
2696
|
+
idFactory: this.idFactory,
|
|
2697
|
+
owner: agent.internal ? "background" : "foreground",
|
|
2698
|
+
summary: turnSummary.uiSummary ?? turnSummary.nextRequiredAction,
|
|
2699
|
+
turnStartSeq: turnSummary.turnStartSeq,
|
|
2700
|
+
turnEndSeq: turnSummary.turnEndSeq,
|
|
2701
|
+
correlationId: agent.persistence?.sessionId ?? agent.runtimeInfo?.sessionId ?? null,
|
|
2702
|
+
modelId: agent.runtimeInfo?.model ?? agent.config.model ?? null,
|
|
2703
|
+
}).ledger,
|
|
2704
|
+
resumeState: touchHarnessResumeState(harnessExtra?.resumeState, {
|
|
2705
|
+
status: "live",
|
|
2706
|
+
source: harnessExtra?.resumeState?.source ?? "snapshot",
|
|
2707
|
+
timestamp,
|
|
2708
|
+
recoveryHint: resolvedOutcome === "provider_error"
|
|
2709
|
+
? "The previous run failed before completion."
|
|
2710
|
+
: resolvedOutcome === "canceled"
|
|
2711
|
+
? "The previous run was interrupted."
|
|
2712
|
+
: null,
|
|
2713
|
+
}),
|
|
2421
2714
|
providerInstructionStrategy: harnessExtra?.providerInstructionStrategy ??
|
|
2422
2715
|
resolveProviderInstructionStrategy(agent.provider),
|
|
2423
2716
|
};
|
|
@@ -2512,6 +2805,12 @@ export class AgentManager {
|
|
|
2512
2805
|
}),
|
|
2513
2806
|
lastCheckpointId: record.id,
|
|
2514
2807
|
harnessVersion: JUNCTION_HARNESS_VERSION,
|
|
2808
|
+
resumeState: touchHarnessResumeState(record.harness.resumeState, {
|
|
2809
|
+
status: "checkpoint_restored",
|
|
2810
|
+
source: "checkpoint",
|
|
2811
|
+
timestamp: new Date().toISOString(),
|
|
2812
|
+
recoveryHint: "Workspace restored from a saved checkpoint.",
|
|
2813
|
+
}),
|
|
2515
2814
|
});
|
|
2516
2815
|
this.recordHarnessEvent(agent, {
|
|
2517
2816
|
kind: "checkpoint_restored",
|