@stigmer/runner 3.40.0 → 3.41.0
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/README.md +3 -1
- package/dist/.build-fingerprint +1 -1
- package/dist/__test-utils__/execution-record-fixture.d.ts +2 -1
- package/dist/__test-utils__/execution-record-fixture.js.map +1 -1
- package/dist/activities/call-agent.js +1 -1
- package/dist/activities/discover-mcp-server.d.ts +7 -1
- package/dist/activities/discover-mcp-server.js +13 -29
- package/dist/activities/discover-mcp-server.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/fold.d.ts +4 -2
- package/dist/activities/execute-cursor/__test-utils__/fold.js +3 -4
- package/dist/activities/execute-cursor/__test-utils__/fold.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +1 -0
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
- package/dist/activities/execute-cursor/approval-policy.d.ts +1 -29
- package/dist/activities/execute-cursor/approval-policy.js +11 -35
- package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
- package/dist/activities/execute-cursor/boundary-rows.js +4 -4
- package/dist/activities/execute-cursor/boundary-rows.js.map +1 -1
- package/dist/activities/execute-cursor/translator.d.ts +12 -17
- package/dist/activities/execute-cursor/translator.js +13 -30
- package/dist/activities/execute-cursor/translator.js.map +1 -1
- package/dist/activities/execute-cursor/turn-stream.js +4 -2
- package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +11 -1
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +22 -3
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -1
- package/dist/activities/execute-deep-agent/inline-publisher.d.ts +33 -2
- package/dist/activities/execute-deep-agent/inline-publisher.js +33 -7
- package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
- package/dist/activities/execute-deep-agent/platform-route.d.ts +48 -13
- package/dist/activities/execute-deep-agent/platform-route.js +87 -19
- package/dist/activities/execute-deep-agent/platform-route.js.map +1 -1
- package/dist/activities/execute-deep-agent/prompt-builder.d.ts +70 -35
- package/dist/activities/execute-deep-agent/prompt-builder.js +70 -55
- package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +20 -3
- package/dist/activities/execute-deep-agent/streaming-side-effects.js +26 -4
- package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
- package/dist/activities/execute-deep-agent/translator.d.ts +5 -4
- package/dist/activities/execute-deep-agent/translator.js +5 -4
- package/dist/activities/execute-deep-agent/translator.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn-settle.js +6 -6
- package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn-setup.d.ts +18 -13
- package/dist/activities/execute-deep-agent/turn-setup.js +42 -36
- package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn-stream.d.ts +10 -6
- package/dist/activities/execute-deep-agent/turn-stream.js +6 -16
- package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn.d.ts +4 -1
- package/dist/activities/execute-deep-agent/turn.js +15 -6
- package/dist/activities/execute-deep-agent/turn.js.map +1 -1
- package/dist/activities/workflow-event-activities.js +2 -0
- package/dist/activities/workflow-event-activities.js.map +1 -1
- package/dist/config.d.ts +9 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/exit-backstop.d.ts +64 -0
- package/dist/exit-backstop.js +79 -0
- package/dist/exit-backstop.js.map +1 -0
- package/dist/harness/terminal-table.d.ts +12 -5
- package/dist/harness/terminal-table.js +13 -13
- package/dist/harness/terminal-table.js.map +1 -1
- package/dist/harness/transcript/builder.d.ts +21 -0
- package/dist/harness/transcript/builder.js +53 -31
- package/dist/harness/transcript/builder.js.map +1 -1
- package/dist/harness/transcript/events.d.ts +22 -26
- package/dist/harness/transcript/events.js +6 -5
- package/dist/harness/transcript/events.js.map +1 -1
- package/dist/harness/turn-context.d.ts +3 -1
- package/dist/harness/turn-context.js +5 -3
- package/dist/harness/turn-context.js.map +1 -1
- package/dist/main.js +10 -4
- package/dist/main.js.map +1 -1
- package/dist/middleware/cost-advisory.d.ts +6 -3
- package/dist/middleware/cost-advisory.js +6 -3
- package/dist/middleware/cost-advisory.js.map +1 -1
- package/dist/middleware/execution-budget.d.ts +22 -7
- package/dist/middleware/execution-budget.js +57 -35
- package/dist/middleware/execution-budget.js.map +1 -1
- package/dist/middleware/index.d.ts +10 -7
- package/dist/middleware/index.js +10 -7
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/types.d.ts +6 -1
- package/dist/runner-manager.js +3 -2
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.d.ts +25 -0
- package/dist/runner.js +41 -14
- package/dist/runner.js.map +1 -1
- package/dist/shared/approval-policy.d.ts +23 -13
- package/dist/shared/approval-policy.js +47 -14
- package/dist/shared/approval-policy.js.map +1 -1
- package/dist/shared/caller-identity.d.ts +4 -5
- package/dist/shared/caller-identity.js +4 -5
- package/dist/shared/caller-identity.js.map +1 -1
- package/dist/shared/channel-attachment.js +3 -2
- package/dist/shared/channel-attachment.js.map +1 -1
- package/dist/shared/connect-backfill.d.ts +2 -1
- package/dist/shared/connect-backfill.js +2 -2
- package/dist/shared/connect-backfill.js.map +1 -1
- package/dist/shared/mcp-resolver.d.ts +10 -1
- package/dist/shared/mcp-resolver.js +11 -2
- package/dist/shared/mcp-resolver.js.map +1 -1
- package/dist/shared/memory-attachment.js +3 -2
- package/dist/shared/memory-attachment.js.map +1 -1
- package/dist/shared/model-error.d.ts +25 -3
- package/dist/shared/model-error.js +78 -10
- package/dist/shared/model-error.js.map +1 -1
- package/dist/shared/platform-server-address.d.ts +71 -0
- package/dist/shared/platform-server-address.js +103 -0
- package/dist/shared/platform-server-address.js.map +1 -0
- package/dist/shared/prompt-sections.d.ts +6 -28
- package/dist/shared/prompt-sections.js +6 -35
- package/dist/shared/prompt-sections.js.map +1 -1
- package/dist/shared/skill-resolver.d.ts +2 -3
- package/dist/shared/skill-resolver.js +2 -3
- package/dist/shared/skill-resolver.js.map +1 -1
- package/dist/shared/synthesized-attachment.d.ts +0 -5
- package/dist/shared/synthesized-attachment.js +0 -12
- package/dist/shared/synthesized-attachment.js.map +1 -1
- package/dist/shared/tool-rounds.d.ts +51 -23
- package/dist/shared/tool-rounds.js +67 -24
- package/dist/shared/tool-rounds.js.map +1 -1
- package/dist/shared/workspace/platform-dir.d.ts +15 -0
- package/dist/shared/workspace/platform-dir.js +18 -1
- package/dist/shared/workspace/platform-dir.js.map +1 -1
- package/dist/shared/workspace/sources/git.js +13 -6
- package/dist/shared/workspace/sources/git.js.map +1 -1
- package/dist/shared/workspace/stigmer-link.d.ts +21 -14
- package/dist/shared/workspace/stigmer-link.js +91 -29
- package/dist/shared/workspace/stigmer-link.js.map +1 -1
- package/dist/tools/url-guard.d.ts +5 -1
- package/dist/tools/url-guard.js +9 -2
- package/dist/tools/url-guard.js.map +1 -1
- package/dist/worker.d.ts +11 -2
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/dist/workflow-engine/tasks/human-input.d.ts +16 -0
- package/dist/workflow-engine/tasks/human-input.js +79 -37
- package/dist/workflow-engine/tasks/human-input.js.map +1 -1
- package/dist/workflow-engine/types.d.ts +14 -4
- package/dist/workflow-engine/types.js.map +1 -1
- package/dist/workflows/call-agent-orchestrator.d.ts +13 -0
- package/dist/workflows/call-agent-orchestrator.js.map +1 -1
- package/dist/workflows/engine-core.d.ts +25 -0
- package/dist/workflows/engine-core.js.map +1 -1
- package/dist/workflows/execute-from-execution.d.ts +3 -0
- package/dist/workflows/execute-from-execution.js.map +1 -1
- package/dist/workflows/human-input-orchestrator.d.ts +9 -12
- package/dist/workflows/human-input-orchestrator.js +12 -44
- package/dist/workflows/human-input-orchestrator.js.map +1 -1
- package/package.json +5 -5
- package/src/__test-utils__/config-fixture.ts +1 -0
- package/src/__test-utils__/execution-record-fixture.ts +2 -1
- package/src/__test-utils__/harness-contract/runtime-contract.ts +5 -3
- package/src/__test-utils__/test-gate.ts +8 -0
- package/src/__tests__/exit-backstop.test.ts +91 -0
- package/src/__tests__/golden-e2e.test.ts +80 -62
- package/src/__tests__/history-encryption-e2e.test.ts +51 -27
- package/src/__tests__/runner.test.ts +60 -2
- package/src/activities/__tests__/classify-tool-approvals.test.ts +1 -0
- package/src/activities/__tests__/discover-mcp-server.hang.test.ts +5 -1
- package/src/activities/__tests__/discover-mcp-server.test.ts +90 -49
- package/src/activities/__tests__/workflow-event-activities.test.ts +28 -7
- package/src/activities/call-agent.ts +1 -1
- package/src/activities/discover-mcp-server.ts +27 -37
- package/src/activities/execute-cursor/__test-utils__/fold.ts +6 -4
- package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +1 -0
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +4 -29
- package/src/activities/execute-cursor/__tests__/hermetic/bypassed-tool-call.test.ts +120 -0
- package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +5 -5
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/file-review-capture.status.json +0 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/resolution-error.status.json +1 -1
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.error-delta.status.json +0 -3
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.output-first.status.json +0 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.status.json +0 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/stall.status.json +0 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +0 -3
- package/src/activities/execute-cursor/__tests__/hermetic/shell-output-delta.test.ts +9 -7
- package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +6 -1
- package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +8 -11
- package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +1 -3
- package/src/activities/execute-cursor/__tests__/translator.test.ts +68 -15
- package/src/activities/execute-cursor/approval-policy.ts +11 -36
- package/src/activities/execute-cursor/boundary-rows.ts +3 -3
- package/src/activities/execute-cursor/translator.ts +15 -32
- package/src/activities/execute-cursor/turn-stream.ts +4 -1
- package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +33 -2
- package/src/activities/execute-deep-agent/__tests__/adapter.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.build-from-plan.prompt.md +1 -27
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.everything.prompt.md +26 -30
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.plan-mode.prompt.md +1 -26
- package/src/activities/execute-deep-agent/__tests__/goldens/user-message.everything.md +36 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +4 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +1 -1
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +1 -1
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +1 -1
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +8 -73
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/resolution-error.status.json +1 -1
- package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +17 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +86 -26
- package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +9 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/thrown-turn-drains-publishes.test.ts +159 -0
- package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +84 -0
- package/src/activities/execute-deep-agent/__tests__/platform-route.test.ts +47 -10
- package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +127 -199
- package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +31 -26
- package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +34 -0
- package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +13 -5
- package/src/activities/execute-deep-agent/inline-publisher.ts +47 -7
- package/src/activities/execute-deep-agent/platform-route.ts +109 -20
- package/src/activities/execute-deep-agent/prompt-builder.ts +104 -92
- package/src/activities/execute-deep-agent/streaming-side-effects.ts +27 -4
- package/src/activities/execute-deep-agent/translator.ts +5 -4
- package/src/activities/execute-deep-agent/turn-settle.ts +6 -6
- package/src/activities/execute-deep-agent/turn-setup.ts +43 -38
- package/src/activities/execute-deep-agent/turn-stream.ts +15 -22
- package/src/activities/execute-deep-agent/turn.ts +16 -7
- package/src/activities/workflow-event-activities.ts +2 -0
- package/src/config.ts +11 -0
- package/src/exit-backstop.ts +97 -0
- package/src/harness/README.md +5 -5
- package/src/harness/terminal-table.ts +13 -13
- package/src/harness/transcript/__tests__/builder.test.ts +93 -21
- package/src/harness/transcript/builder.ts +53 -30
- package/src/harness/transcript/events.ts +22 -27
- package/src/harness/turn-context.ts +5 -3
- package/src/main.ts +9 -5
- package/src/middleware/__tests__/execution-budget.test.ts +181 -103
- package/src/middleware/cost-advisory.ts +6 -3
- package/src/middleware/execution-budget.ts +60 -34
- package/src/middleware/index.ts +10 -7
- package/src/middleware/types.ts +6 -1
- package/src/runner-manager.ts +3 -2
- package/src/runner.ts +57 -15
- package/src/shared/__tests__/approval-policy.test.ts +41 -0
- package/src/shared/__tests__/artifact-storage.test.ts +1 -0
- package/src/shared/__tests__/connect-backfill.test.ts +15 -11
- package/src/shared/__tests__/mcp-resolver.test.ts +72 -10
- package/src/shared/__tests__/model-error.test.ts +71 -3
- package/src/shared/__tests__/platform-server-address.test.ts +122 -0
- package/src/shared/__tests__/prompt-sections.test.ts +2 -54
- package/src/shared/__tests__/tool-rounds.test.ts +64 -14
- package/src/shared/approval-policy.ts +57 -14
- package/src/shared/caller-identity.ts +4 -5
- package/src/shared/channel-attachment.ts +2 -2
- package/src/shared/connect-backfill.ts +5 -1
- package/src/shared/mcp-resolver.ts +11 -1
- package/src/shared/memory-attachment.ts +2 -2
- package/src/shared/model-error.ts +87 -9
- package/src/shared/platform-server-address.ts +123 -0
- package/src/shared/prompt-sections.ts +6 -55
- package/src/shared/skill-resolver.ts +2 -3
- package/src/shared/synthesized-attachment.ts +0 -12
- package/src/shared/tool-rounds.ts +70 -24
- package/src/shared/workspace/__tests__/git-source.test.ts +11 -5
- package/src/shared/workspace/__tests__/stigmer-link.test.ts +107 -10
- package/src/shared/workspace/platform-dir.ts +19 -1
- package/src/shared/workspace/sources/git.ts +14 -8
- package/src/shared/workspace/stigmer-link.ts +99 -29
- package/src/tools/__tests__/url-guard.test.ts +19 -1
- package/src/tools/url-guard.ts +9 -1
- package/src/worker.ts +12 -2
- package/src/workflow-engine/__tests__/golden-execution.test.ts +2 -1
- package/src/workflow-engine/__tests__/loader.test.ts +1 -1
- package/src/workflow-engine/__tests__/tasks/human-input.test.ts +133 -57
- package/src/workflow-engine/tasks/human-input.ts +92 -42
- package/src/workflow-engine/types.ts +16 -4
- package/src/workflows/call-agent-orchestrator.ts +7 -0
- package/src/workflows/engine-core.ts +18 -0
- package/src/workflows/execute-from-execution.ts +2 -1
- package/src/workflows/human-input-orchestrator.ts +15 -56
- package/dist/shared/skill-relevance.d.ts +0 -65
- package/dist/shared/skill-relevance.js +0 -175
- package/dist/shared/skill-relevance.js.map +0 -1
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.skills-below-threshold.prompt.md +0 -146
- package/src/shared/__tests__/skill-relevance.test.ts +0 -292
- package/src/shared/skill-relevance.ts +0 -222
|
@@ -32,6 +32,16 @@
|
|
|
32
32
|
* activation and attached to the approval_requested event — inline when
|
|
33
33
|
* small, as an artifact reference when at/above the promotion threshold —
|
|
34
34
|
* so the approval record captures exactly what the reviewer saw.
|
|
35
|
+
*
|
|
36
|
+
* Timeout policy lives here, whole. `ctx.awaitHumanInput()` only reports
|
|
37
|
+
* that the timer won; this module decides what that means. approve, deny and
|
|
38
|
+
* escalate resolve the gate to a declared outcome. fail logs an
|
|
39
|
+
* approval_resolved with `auto_resolved` set and no outcome, then fails the
|
|
40
|
+
* task with the error it has always raised, so every approval_requested in
|
|
41
|
+
* the event log is closed by an approval_resolved (stigmer/stigmer#1442).
|
|
42
|
+
* That emit is a new workflow command on an existing path, so it runs behind
|
|
43
|
+
* {@link HUMAN_INPUT_FAIL_TIMEOUT_RESOLVED_PATCH}: a history recorded before
|
|
44
|
+
* it replays the old order (no emit, then the throw).
|
|
35
45
|
*/
|
|
36
46
|
|
|
37
47
|
import type {
|
|
@@ -41,6 +51,9 @@ import type {
|
|
|
41
51
|
WorkflowState,
|
|
42
52
|
TaskExecutionContext,
|
|
43
53
|
HumanInputResult,
|
|
54
|
+
HumanInputResponse,
|
|
55
|
+
HumanInputTimeout,
|
|
56
|
+
HumanInputTimeoutPolicy,
|
|
44
57
|
} from "../types.js";
|
|
45
58
|
import { resolveConfigExpressions, resolveEmbeddedExpressions } from "../resolve.js";
|
|
46
59
|
import { isStrictExpr } from "../expression-utils.js";
|
|
@@ -49,6 +62,13 @@ const SIGNAL_PREFIX = "human_input_";
|
|
|
49
62
|
const DEFAULT_TIMEOUT_SECONDS = 0;
|
|
50
63
|
const DEFAULT_ON_TIMEOUT = "fail" as const;
|
|
51
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Temporal patch id for logging approval_resolved before a fail-policy
|
|
67
|
+
* timeout fails the task. Wire bytes: a rename makes every history that
|
|
68
|
+
* carries the marker replay down the old path.
|
|
69
|
+
*/
|
|
70
|
+
export const HUMAN_INPUT_FAIL_TIMEOUT_RESOLVED_PATCH = "human-input-fail-timeout-resolved";
|
|
71
|
+
|
|
52
72
|
/**
|
|
53
73
|
* Executes a human_input task. Called from `runSingleTask` in do-executor
|
|
54
74
|
* when the task kind is "human_input".
|
|
@@ -97,29 +117,22 @@ export async function executeHumanInputTask(
|
|
|
97
117
|
]);
|
|
98
118
|
}
|
|
99
119
|
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
onTimeout,
|
|
105
|
-
}),
|
|
106
|
-
config.outcomes,
|
|
107
|
-
);
|
|
120
|
+
const response = await ctx.awaitHumanInput({ signalName, timeoutSeconds });
|
|
121
|
+
const result = isTimeout(response)
|
|
122
|
+
? resolveTimeout(onTimeout, config.outcomes)
|
|
123
|
+
: response;
|
|
108
124
|
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
resolvedByActor: result.reviewer_actor,
|
|
117
|
-
comment: "",
|
|
118
|
-
waitDurationMs: Date.now() - approvalRequestedAt,
|
|
119
|
-
autoResolved: result.auto_resolved ?? false,
|
|
120
|
-
}]);
|
|
125
|
+
if (result === undefined) {
|
|
126
|
+
if (ctx.isPatched?.(HUMAN_INPUT_FAIL_TIMEOUT_RESOLVED_PATCH) ?? true) {
|
|
127
|
+
await emitApprovalResolved(ctx, taskName, approvalRequestedAt, {
|
|
128
|
+
outcome: "", auto_resolved: true, reason: "timeout",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
throw new Error(`human_input task timed out waiting for signal '${signalName}'`);
|
|
121
132
|
}
|
|
122
133
|
|
|
134
|
+
await emitApprovalResolved(ctx, taskName, approvalRequestedAt, result);
|
|
135
|
+
|
|
123
136
|
state.addData({ [taskName]: result });
|
|
124
137
|
|
|
125
138
|
const selectedOutcome = config.outcomes?.find(o => o.name === result.outcome);
|
|
@@ -136,35 +149,72 @@ function validateConfig(config: HumanInputConfig, taskName: string): void {
|
|
|
136
149
|
}
|
|
137
150
|
}
|
|
138
151
|
|
|
152
|
+
function isTimeout(response: HumanInputResponse): response is HumanInputTimeout {
|
|
153
|
+
return "timedOut" in response;
|
|
154
|
+
}
|
|
155
|
+
|
|
139
156
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
157
|
+
* The decision a timeout policy makes, or `undefined` when the policy is
|
|
158
|
+
* fail and the gate ends without one.
|
|
159
|
+
*
|
|
160
|
+
* With custom outcomes the proto contract (HumanInputTaskConfig.outcomes doc)
|
|
161
|
+
* applies: auto-approve resolves to the FIRST declared outcome and auto-deny
|
|
162
|
+
* to the LAST, so `then` routing and downstream outcome switches see declared
|
|
163
|
+
* outcome names, never approve/deny words a reviewer of a custom-outcome gate
|
|
164
|
+
* was never offered. Binary gates (no custom outcomes) keep plain approve/deny.
|
|
147
165
|
*
|
|
148
166
|
* Escalate needs no remapping by construction (stigmer/stigmer#781): its
|
|
149
|
-
* policy word IS the declared outcome's name
|
|
167
|
+
* policy word IS the declared outcome's name. The loader (and the server
|
|
150
168
|
* validator) only accept the escalate policy when an outcome named
|
|
151
|
-
* "escalate" with `then` exists, so the caller's ordinary name lookup
|
|
152
|
-
*
|
|
169
|
+
* "escalate" with `then` exists, so the caller's ordinary name lookup routes
|
|
170
|
+
* the escalation branch.
|
|
153
171
|
*/
|
|
154
|
-
function
|
|
155
|
-
|
|
172
|
+
function resolveTimeout(
|
|
173
|
+
policy: HumanInputTimeoutPolicy,
|
|
156
174
|
outcomes: HumanInputConfig["outcomes"],
|
|
157
|
-
): HumanInputResult {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
175
|
+
): HumanInputResult | undefined {
|
|
176
|
+
const decided = (outcome: string): HumanInputResult => ({
|
|
177
|
+
outcome, auto_resolved: true, reason: "timeout",
|
|
178
|
+
});
|
|
179
|
+
switch (policy) {
|
|
180
|
+
case "approve":
|
|
181
|
+
return decided(outcomes?.length ? outcomes[0].name : "approve");
|
|
182
|
+
case "deny":
|
|
183
|
+
return decided(outcomes?.length ? outcomes[outcomes.length - 1].name : "deny");
|
|
184
|
+
case "escalate":
|
|
185
|
+
return decided("escalate");
|
|
186
|
+
case "fail":
|
|
187
|
+
return undefined;
|
|
188
|
+
default: {
|
|
189
|
+
const unreachable: never = policy;
|
|
190
|
+
throw new Error(`human_input: unknown timeout policy '${String(unreachable)}'`);
|
|
191
|
+
}
|
|
166
192
|
}
|
|
167
|
-
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Logs how the gate's approval ended. A timeout's decision carries no
|
|
197
|
+
* reviewer, no comment and `auto_resolved`; a fail timeout's carries no
|
|
198
|
+
* outcome either.
|
|
199
|
+
*/
|
|
200
|
+
async function emitApprovalResolved(
|
|
201
|
+
ctx: TaskExecutionContext,
|
|
202
|
+
taskName: string,
|
|
203
|
+
approvalRequestedAt: number,
|
|
204
|
+
result: HumanInputResult,
|
|
205
|
+
): Promise<void> {
|
|
206
|
+
if (!ctx.emitEvents) return;
|
|
207
|
+
await ctx.emitEvents([{
|
|
208
|
+
type: "approval_resolved",
|
|
209
|
+
taskName,
|
|
210
|
+
occurredAt: new Date().toISOString(),
|
|
211
|
+
outcome: result.outcome,
|
|
212
|
+
resolvedBy: result.reviewer ?? "",
|
|
213
|
+
resolvedByActor: result.reviewer_actor,
|
|
214
|
+
comment: result.comment ?? "",
|
|
215
|
+
waitDurationMs: Date.now() - approvalRequestedAt,
|
|
216
|
+
autoResolved: result.auto_resolved ?? false,
|
|
217
|
+
}]);
|
|
168
218
|
}
|
|
169
219
|
|
|
170
220
|
interface ResolvedReviewPayload {
|
|
@@ -561,10 +561,20 @@ export interface RunWorkflowExecutionConfig {
|
|
|
561
561
|
}
|
|
562
562
|
|
|
563
563
|
/**
|
|
564
|
-
* Blocks workflow execution until a human provides input via signal
|
|
565
|
-
*
|
|
564
|
+
* Blocks workflow execution until a human provides input via signal, or
|
|
565
|
+
* until the timeout elapses. Reports which happened and nothing more: the
|
|
566
|
+
* timeout policy (fail, approve, deny, escalate) is the kernel's to apply
|
|
567
|
+
* (`tasks/human-input.ts`), so one module owns every way a gate can end.
|
|
566
568
|
*/
|
|
567
|
-
export type AwaitHumanInputFn = (config: HumanInputExecutionConfig) => Promise<
|
|
569
|
+
export type AwaitHumanInputFn = (config: HumanInputExecutionConfig) => Promise<HumanInputResponse>;
|
|
570
|
+
|
|
571
|
+
/** What a wait at a human_input gate ended with: the reviewer's decision, or the timeout. */
|
|
572
|
+
export type HumanInputResponse = HumanInputResult | HumanInputTimeout;
|
|
573
|
+
|
|
574
|
+
/** The gate's timeout elapsed before any decision arrived. */
|
|
575
|
+
export interface HumanInputTimeout {
|
|
576
|
+
readonly timedOut: true;
|
|
577
|
+
}
|
|
568
578
|
|
|
569
579
|
/**
|
|
570
580
|
* Internal timeout-policy vocabulary — the loader-normalized form of the
|
|
@@ -577,8 +587,8 @@ export type HumanInputTimeoutPolicy = "fail" | "approve" | "deny" | "escalate";
|
|
|
577
587
|
|
|
578
588
|
export interface HumanInputExecutionConfig {
|
|
579
589
|
readonly signalName: string;
|
|
590
|
+
/** Seconds to wait before reporting a {@link HumanInputTimeout}; 0 waits indefinitely. */
|
|
580
591
|
readonly timeoutSeconds: number;
|
|
581
|
-
readonly onTimeout: HumanInputTimeoutPolicy;
|
|
582
592
|
}
|
|
583
593
|
|
|
584
594
|
export interface HumanInputResult {
|
|
@@ -594,6 +604,8 @@ export interface HumanInputResult {
|
|
|
594
604
|
readonly reviewer_actor?: HumanInputReviewerActor;
|
|
595
605
|
readonly responded_at?: string;
|
|
596
606
|
readonly form_data?: Record<string, unknown>;
|
|
607
|
+
/** Free-text comment the reviewer attached. Absent when none was written, and always on a timeout. */
|
|
608
|
+
readonly comment?: string;
|
|
597
609
|
readonly auto_resolved?: boolean;
|
|
598
610
|
readonly reason?: string;
|
|
599
611
|
}
|
|
@@ -65,6 +65,13 @@ type AgentActivities = ReturnType<typeof createCallAgentActivities>;
|
|
|
65
65
|
type StatusActivities = ReturnType<typeof createCallAgentStatusActivities>;
|
|
66
66
|
type EventActivities = ReturnType<typeof createWorkflowEventActivities>;
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Every activity an agent call schedules. The engine's own activity type
|
|
70
|
+
* includes it, so a worker (or a test mock) that registers the engine's
|
|
71
|
+
* activities is checked against this list by the compiler.
|
|
72
|
+
*/
|
|
73
|
+
export type AgentCallActivities = AgentActivities & StatusActivities & EventActivities;
|
|
74
|
+
|
|
68
75
|
// The 1h ceiling is a deliberate platform constant, not a user knob: the
|
|
69
76
|
// old AgentCallTaskConfig declared a per-task `timeout` that nothing ever
|
|
70
77
|
// read, and #358 deleted it rather than declaring what isn't honored. The
|
|
@@ -28,6 +28,7 @@ import type { createRunCommandActivities } from "../activities/run-command.js";
|
|
|
28
28
|
import type { createWorkflowEventActivities } from "../activities/workflow-event-activities.js";
|
|
29
29
|
import type { createPromoteTaskOutputActivities } from "../activities/promote-task-output.js";
|
|
30
30
|
import { orchestrateAgentCall } from "./call-agent-orchestrator.js";
|
|
31
|
+
import type { AgentCallActivities } from "./call-agent-orchestrator.js";
|
|
31
32
|
import { orchestrateListenTask } from "./listen-orchestrator.js";
|
|
32
33
|
import { orchestrateRunWorkflow } from "./run-orchestrator.js";
|
|
33
34
|
import { orchestrateHumanInput } from "./human-input-orchestrator.js";
|
|
@@ -65,6 +66,23 @@ type RunActivities = ReturnType<typeof createRunCommandActivities>;
|
|
|
65
66
|
type EventActivities = ReturnType<typeof createWorkflowEventActivities>;
|
|
66
67
|
type PromoteActivities = ReturnType<typeof createPromoteTaskOutputActivities>;
|
|
67
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Every activity the engine can schedule, by name. A worker that runs the
|
|
71
|
+
* engine must register each one; the test mocks declare
|
|
72
|
+
* `satisfies Record<keyof EngineActivities, unknown>` so a new activity
|
|
73
|
+
* without a mock fails the typecheck instead of failing the smoke run at
|
|
74
|
+
* run time (stigmer#1300: four missing local activities made the golden
|
|
75
|
+
* suite report passes for months without running a golden).
|
|
76
|
+
*/
|
|
77
|
+
export type EngineActivities = EvalActivities &
|
|
78
|
+
HttpActivities &
|
|
79
|
+
GrpcActivities &
|
|
80
|
+
FunctionActivities &
|
|
81
|
+
RunActivities &
|
|
82
|
+
EventActivities &
|
|
83
|
+
PromoteActivities &
|
|
84
|
+
AgentCallActivities;
|
|
85
|
+
|
|
68
86
|
const evalProxy = proxyLocalActivities<EvalActivities>({
|
|
69
87
|
startToCloseTimeout: "10s",
|
|
70
88
|
});
|
|
@@ -31,7 +31,8 @@ import type { createHydrateWorkflowActivities } from "../activities/hydrate-work
|
|
|
31
31
|
// Hydration Activity Proxy
|
|
32
32
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
/** The activity this entry point schedules before it hands the model to the engine. */
|
|
35
|
+
export type HydrateActivities = ReturnType<typeof createHydrateWorkflowActivities>;
|
|
35
36
|
|
|
36
37
|
const hydrateProxy = proxyActivities<HydrateActivities>({
|
|
37
38
|
startToCloseTimeout: "2m",
|
|
@@ -2,18 +2,15 @@
|
|
|
2
2
|
* Human input orchestrator — Temporal workflow-layer HITL signal handler.
|
|
3
3
|
*
|
|
4
4
|
* Registers a signal handler for the human_input task, then blocks until
|
|
5
|
-
* either the signal arrives or the timeout fires
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
*
|
|
11
|
-
* contract (stigmer/stigmer#781) the loader guarantees the gate declares
|
|
12
|
-
* an outcome with that exact name and a `then` branch, so the executor's
|
|
13
|
-
* ordinary outcome routing takes the escalation path.
|
|
5
|
+
* either the signal arrives or the timeout fires, and reports which one
|
|
6
|
+
* happened: the reviewer's payload, or a timeout. It applies no timeout
|
|
7
|
+
* policy. What a timeout means (fail the task, or resolve the gate to a
|
|
8
|
+
* declared outcome) is decided in the kernel (`workflow-engine/tasks/
|
|
9
|
+
* human-input.ts`), which also logs how the approval ended, so the two
|
|
10
|
+
* cannot drift apart across layers.
|
|
14
11
|
*
|
|
15
12
|
* Signal payload shape:
|
|
16
|
-
* { outcome, form_data?, reviewer, reviewer_actor?, responded_at }
|
|
13
|
+
* { outcome, form_data?, comment?, reviewer, reviewer_actor?, responded_at }
|
|
17
14
|
*
|
|
18
15
|
* TEMPORAL SANDBOX: This file runs inside the deterministic workflow isolate.
|
|
19
16
|
*/
|
|
@@ -22,17 +19,20 @@ import { defineSignal, setHandler, condition } from "@temporalio/workflow";
|
|
|
22
19
|
|
|
23
20
|
import type {
|
|
24
21
|
HumanInputExecutionConfig,
|
|
22
|
+
HumanInputResponse,
|
|
25
23
|
HumanInputResult,
|
|
26
|
-
|
|
24
|
+
HumanInputTimeout,
|
|
27
25
|
} from "../workflow-engine/types.js";
|
|
28
26
|
|
|
27
|
+
const TIMED_OUT: HumanInputTimeout = { timedOut: true };
|
|
28
|
+
|
|
29
29
|
/**
|
|
30
30
|
* Orchestrates a human_input task — blocks until signal or timeout.
|
|
31
31
|
*/
|
|
32
32
|
export async function orchestrateHumanInput(
|
|
33
33
|
config: HumanInputExecutionConfig,
|
|
34
|
-
): Promise<
|
|
35
|
-
const { signalName, timeoutSeconds
|
|
34
|
+
): Promise<HumanInputResponse> {
|
|
35
|
+
const { signalName, timeoutSeconds } = config;
|
|
36
36
|
|
|
37
37
|
let payload: HumanInputResult | undefined;
|
|
38
38
|
let received = false;
|
|
@@ -44,52 +44,11 @@ export async function orchestrateHumanInput(
|
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
if (timeoutSeconds > 0) {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (!completed) {
|
|
51
|
-
return handleTimeout(signalName, onTimeout);
|
|
52
|
-
}
|
|
47
|
+
const completed = await condition(() => received, timeoutSeconds * 1000);
|
|
48
|
+
if (!completed) return TIMED_OUT;
|
|
53
49
|
} else {
|
|
54
50
|
await condition(() => received);
|
|
55
51
|
}
|
|
56
52
|
|
|
57
53
|
return payload!;
|
|
58
54
|
}
|
|
59
|
-
|
|
60
|
-
function handleTimeout(
|
|
61
|
-
signalName: string,
|
|
62
|
-
policy: HumanInputTimeoutPolicy,
|
|
63
|
-
): HumanInputResult {
|
|
64
|
-
switch (policy) {
|
|
65
|
-
case "approve":
|
|
66
|
-
return {
|
|
67
|
-
outcome: "approve",
|
|
68
|
-
auto_resolved: true,
|
|
69
|
-
reason: "timeout",
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
case "deny":
|
|
73
|
-
return {
|
|
74
|
-
outcome: "deny",
|
|
75
|
-
auto_resolved: true,
|
|
76
|
-
reason: "timeout",
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
case "escalate":
|
|
80
|
-
// The outcome word IS the declared outcome's name (loader-enforced),
|
|
81
|
-
// so no positional remapping happens downstream — the executor's name
|
|
82
|
-
// lookup routes the escalation branch directly.
|
|
83
|
-
return {
|
|
84
|
-
outcome: "escalate",
|
|
85
|
-
auto_resolved: true,
|
|
86
|
-
reason: "timeout",
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
case "fail":
|
|
90
|
-
default:
|
|
91
|
-
throw new Error(
|
|
92
|
-
`human_input task timed out waiting for signal '${signalName}'`,
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skill relevance scoring for smart context filtering.
|
|
3
|
-
*
|
|
4
|
-
* Uses a BM25-inspired algorithm to score each skill's relevance
|
|
5
|
-
* against the user message. When an agent has many skills configured,
|
|
6
|
-
* low-relevance skills are excluded from the system prompt to improve
|
|
7
|
-
* signal quality.
|
|
8
|
-
*
|
|
9
|
-
* The progressive disclosure model means only skill metadata
|
|
10
|
-
* (~50-70 tokens per skill) lives in the prompt, so filtering
|
|
11
|
-
* primarily reduces noise rather than saving tokens.
|
|
12
|
-
*
|
|
13
|
-
* BM25 (Best Matching 25) is a lightweight, well-understood ranking
|
|
14
|
-
* function from information retrieval. It uses term frequency saturation
|
|
15
|
-
* and document length normalisation — both important when matching a
|
|
16
|
-
* short user message (query) against short skill metadata (documents of
|
|
17
|
-
* 5-20 tokens each).
|
|
18
|
-
*
|
|
19
|
-
* No external dependencies required.
|
|
20
|
-
*/
|
|
21
|
-
/** Relevance filtering activates only when the agent has at least this many skills. */
|
|
22
|
-
export declare const SKILL_COUNT_THRESHOLD = 8;
|
|
23
|
-
/**
|
|
24
|
-
* Split text into lowercase tokens, dropping stop words and
|
|
25
|
-
* single-character fragments.
|
|
26
|
-
*/
|
|
27
|
-
export declare function tokenize(text: string): string[];
|
|
28
|
-
export interface ScoredSkill {
|
|
29
|
-
readonly index: number;
|
|
30
|
-
readonly name: string;
|
|
31
|
-
readonly score: number;
|
|
32
|
-
}
|
|
33
|
-
export interface SkillFilterResult {
|
|
34
|
-
/** Indices of skills to include in the prompt (original order). */
|
|
35
|
-
readonly includedIndices: number[];
|
|
36
|
-
/** Indices of skills excluded from the prompt (original order). */
|
|
37
|
-
readonly excludedIndices: number[];
|
|
38
|
-
/** Names of excluded skills (sorted alphabetically). */
|
|
39
|
-
readonly excludedNames: string[];
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Score each skill's relevance to the user message using BM25.
|
|
43
|
-
*
|
|
44
|
-
* Each skill's name and description are concatenated into a document
|
|
45
|
-
* and scored against the query terms extracted from the user message.
|
|
46
|
-
*
|
|
47
|
-
* Returns ScoredSkill list ordered by descending score (ties broken alphabetically).
|
|
48
|
-
*/
|
|
49
|
-
export declare function scoreSkills(userMessage: string, skillNames: readonly string[], skillDescriptions: readonly string[], options?: {
|
|
50
|
-
k1?: number;
|
|
51
|
-
b?: number;
|
|
52
|
-
}): ScoredSkill[];
|
|
53
|
-
/**
|
|
54
|
-
* Partition skills into included / excluded based on relevance.
|
|
55
|
-
*
|
|
56
|
-
* Below the threshold, every skill is included unconditionally.
|
|
57
|
-
* Above the threshold, skills whose BM25 score is zero (no query
|
|
58
|
-
* term overlap at all) are moved to the excluded set.
|
|
59
|
-
*
|
|
60
|
-
* A safety floor guarantees that at least half the skills are
|
|
61
|
-
* always included, regardless of scores.
|
|
62
|
-
*/
|
|
63
|
-
export declare function filterSkills(userMessage: string, skillNames: readonly string[], skillDescriptions: readonly string[], options?: {
|
|
64
|
-
threshold?: number;
|
|
65
|
-
}): SkillFilterResult;
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skill relevance scoring for smart context filtering.
|
|
3
|
-
*
|
|
4
|
-
* Uses a BM25-inspired algorithm to score each skill's relevance
|
|
5
|
-
* against the user message. When an agent has many skills configured,
|
|
6
|
-
* low-relevance skills are excluded from the system prompt to improve
|
|
7
|
-
* signal quality.
|
|
8
|
-
*
|
|
9
|
-
* The progressive disclosure model means only skill metadata
|
|
10
|
-
* (~50-70 tokens per skill) lives in the prompt, so filtering
|
|
11
|
-
* primarily reduces noise rather than saving tokens.
|
|
12
|
-
*
|
|
13
|
-
* BM25 (Best Matching 25) is a lightweight, well-understood ranking
|
|
14
|
-
* function from information retrieval. It uses term frequency saturation
|
|
15
|
-
* and document length normalisation — both important when matching a
|
|
16
|
-
* short user message (query) against short skill metadata (documents of
|
|
17
|
-
* 5-20 tokens each).
|
|
18
|
-
*
|
|
19
|
-
* No external dependencies required.
|
|
20
|
-
*/
|
|
21
|
-
/** Relevance filtering activates only when the agent has at least this many skills. */
|
|
22
|
-
export const SKILL_COUNT_THRESHOLD = 8;
|
|
23
|
-
/** Term-frequency saturation. Higher values weight repeated terms more. */
|
|
24
|
-
const BM25_K1 = 1.5;
|
|
25
|
-
/** Length normalisation. 0 = none, 1 = full relative to average doc length. */
|
|
26
|
-
const BM25_B = 0.75;
|
|
27
|
-
const STOP_WORDS = new Set([
|
|
28
|
-
"a", "an", "the", "is", "are", "was", "were", "be", "been",
|
|
29
|
-
"being", "have", "has", "had", "do", "does", "did", "will",
|
|
30
|
-
"would", "could", "should", "may", "might", "shall", "can",
|
|
31
|
-
"to", "of", "in", "for", "on", "with", "at", "by", "from",
|
|
32
|
-
"as", "into", "through", "during", "before", "after", "above",
|
|
33
|
-
"below", "between", "and", "but", "or", "nor", "not", "so",
|
|
34
|
-
"yet", "both", "either", "neither", "each", "every", "all",
|
|
35
|
-
"any", "few", "more", "most", "some", "such", "no", "only",
|
|
36
|
-
"own", "same", "than", "too", "very", "just", "because",
|
|
37
|
-
"about", "up", "out", "if", "then", "that", "this", "these",
|
|
38
|
-
"those", "it", "its", "i", "me", "my", "we", "our", "you",
|
|
39
|
-
"your", "he", "she", "they", "them", "their", "what", "which",
|
|
40
|
-
"who", "whom", "how", "when", "where", "why",
|
|
41
|
-
]);
|
|
42
|
-
const TOKEN_PATTERN = /[a-z0-9]+/g;
|
|
43
|
-
// ─── Tokenisation ────────────────────────────────────────────────────────
|
|
44
|
-
/**
|
|
45
|
-
* Split text into lowercase tokens, dropping stop words and
|
|
46
|
-
* single-character fragments.
|
|
47
|
-
*/
|
|
48
|
-
export function tokenize(text) {
|
|
49
|
-
const matches = text.toLowerCase().match(TOKEN_PATTERN);
|
|
50
|
-
if (!matches)
|
|
51
|
-
return [];
|
|
52
|
-
return matches.filter(tok => !STOP_WORDS.has(tok) && tok.length > 1);
|
|
53
|
-
}
|
|
54
|
-
// ─── BM25 scoring ────────────────────────────────────────────────────────
|
|
55
|
-
/**
|
|
56
|
-
* Score each skill's relevance to the user message using BM25.
|
|
57
|
-
*
|
|
58
|
-
* Each skill's name and description are concatenated into a document
|
|
59
|
-
* and scored against the query terms extracted from the user message.
|
|
60
|
-
*
|
|
61
|
-
* Returns ScoredSkill list ordered by descending score (ties broken alphabetically).
|
|
62
|
-
*/
|
|
63
|
-
export function scoreSkills(userMessage, skillNames, skillDescriptions, options) {
|
|
64
|
-
const k1 = options?.k1 ?? BM25_K1;
|
|
65
|
-
const b = options?.b ?? BM25_B;
|
|
66
|
-
const queryTerms = tokenize(userMessage);
|
|
67
|
-
const n = skillNames.length;
|
|
68
|
-
if (n === 0)
|
|
69
|
-
return [];
|
|
70
|
-
if (queryTerms.length === 0) {
|
|
71
|
-
return skillNames.map((name, index) => ({ index, name, score: 0.0 }));
|
|
72
|
-
}
|
|
73
|
-
// Build per-skill term-frequency maps.
|
|
74
|
-
const docs = [];
|
|
75
|
-
const docLengths = [];
|
|
76
|
-
for (let i = 0; i < n; i++) {
|
|
77
|
-
const tokens = tokenize(`${skillNames[i]} ${skillDescriptions[i]}`);
|
|
78
|
-
const tf = new Map();
|
|
79
|
-
for (const tok of tokens) {
|
|
80
|
-
tf.set(tok, (tf.get(tok) ?? 0) + 1);
|
|
81
|
-
}
|
|
82
|
-
docs.push(tf);
|
|
83
|
-
docLengths.push(tokens.length);
|
|
84
|
-
}
|
|
85
|
-
const avgdl = docLengths.reduce((sum, l) => sum + l, 0) / n;
|
|
86
|
-
// IDF per unique query term.
|
|
87
|
-
const uniqueQueryTerms = new Set(queryTerms);
|
|
88
|
-
const idf = new Map();
|
|
89
|
-
for (const term of uniqueQueryTerms) {
|
|
90
|
-
let df = 0;
|
|
91
|
-
for (const doc of docs) {
|
|
92
|
-
if (doc.has(term))
|
|
93
|
-
df++;
|
|
94
|
-
}
|
|
95
|
-
idf.set(term, Math.max(0.0, Math.log((n - df + 0.5) / (df + 0.5) + 1.0)));
|
|
96
|
-
}
|
|
97
|
-
// Score each document.
|
|
98
|
-
const results = [];
|
|
99
|
-
for (let i = 0; i < n; i++) {
|
|
100
|
-
const docTf = docs[i];
|
|
101
|
-
const dl = docLengths[i];
|
|
102
|
-
let score = 0.0;
|
|
103
|
-
for (const term of queryTerms) {
|
|
104
|
-
const tfVal = docTf.get(term);
|
|
105
|
-
if (tfVal === undefined)
|
|
106
|
-
continue;
|
|
107
|
-
const numerator = tfVal * (k1 + 1);
|
|
108
|
-
const denominator = tfVal + k1 * (1 - b + b * dl / avgdl);
|
|
109
|
-
score += (idf.get(term) ?? 0.0) * numerator / denominator;
|
|
110
|
-
}
|
|
111
|
-
results.push({ index: i, name: skillNames[i], score });
|
|
112
|
-
}
|
|
113
|
-
results.sort((a, b_) => {
|
|
114
|
-
if (b_.score !== a.score)
|
|
115
|
-
return b_.score - a.score;
|
|
116
|
-
return a.name.localeCompare(b_.name);
|
|
117
|
-
});
|
|
118
|
-
return results;
|
|
119
|
-
}
|
|
120
|
-
// ─── Filtering ───────────────────────────────────────────────────────────
|
|
121
|
-
/**
|
|
122
|
-
* Partition skills into included / excluded based on relevance.
|
|
123
|
-
*
|
|
124
|
-
* Below the threshold, every skill is included unconditionally.
|
|
125
|
-
* Above the threshold, skills whose BM25 score is zero (no query
|
|
126
|
-
* term overlap at all) are moved to the excluded set.
|
|
127
|
-
*
|
|
128
|
-
* A safety floor guarantees that at least half the skills are
|
|
129
|
-
* always included, regardless of scores.
|
|
130
|
-
*/
|
|
131
|
-
export function filterSkills(userMessage, skillNames, skillDescriptions, options) {
|
|
132
|
-
const threshold = options?.threshold ?? SKILL_COUNT_THRESHOLD;
|
|
133
|
-
const n = skillNames.length;
|
|
134
|
-
const allIndices = Array.from({ length: n }, (_, i) => i);
|
|
135
|
-
if (n < threshold) {
|
|
136
|
-
return {
|
|
137
|
-
includedIndices: allIndices,
|
|
138
|
-
excludedIndices: [],
|
|
139
|
-
excludedNames: [],
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
const scored = scoreSkills(userMessage, skillNames, skillDescriptions);
|
|
143
|
-
const included = [];
|
|
144
|
-
const excluded = [];
|
|
145
|
-
const excludedNames = [];
|
|
146
|
-
for (const s of scored) {
|
|
147
|
-
if (s.score > 0.0) {
|
|
148
|
-
included.push(s.index);
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
excluded.push(s.index);
|
|
152
|
-
excludedNames.push(s.name);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
// Safety: always keep at least half the skills.
|
|
156
|
-
const minIncluded = Math.max(1, Math.floor(n / 2));
|
|
157
|
-
if (included.length < minIncluded) {
|
|
158
|
-
const deficit = minIncluded - included.length;
|
|
159
|
-
const reInclude = excluded.splice(0, deficit);
|
|
160
|
-
included.push(...reInclude);
|
|
161
|
-
for (const idx of reInclude) {
|
|
162
|
-
const nameIdx = excludedNames.indexOf(skillNames[idx]);
|
|
163
|
-
if (nameIdx !== -1)
|
|
164
|
-
excludedNames.splice(nameIdx, 1);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
included.sort((a, b) => a - b);
|
|
168
|
-
excluded.sort((a, b) => a - b);
|
|
169
|
-
return {
|
|
170
|
-
includedIndices: included,
|
|
171
|
-
excludedIndices: excluded,
|
|
172
|
-
excludedNames: excludedNames.sort(),
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
//# sourceMappingURL=skill-relevance.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"skill-relevance.js","sourceRoot":"","sources":["../../src/shared/skill-relevance.ts"],"names":[],"mappings":"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"}
|