@namzu/sdk 62.12.0 → 62.14.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/CHANGELOG.md +31 -0
- package/dist/agents/file-definitions.d.ts +7 -0
- package/dist/agents/file-definitions.d.ts.map +1 -1
- package/dist/agents/file-definitions.js +20 -0
- package/dist/agents/file-definitions.js.map +1 -1
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +1 -1
- package/dist/agents/index.js.map +1 -1
- package/dist/bridge/acp/server.d.ts +8 -1
- package/dist/bridge/acp/server.d.ts.map +1 -1
- package/dist/bridge/acp/server.js +12 -0
- package/dist/bridge/acp/server.js.map +1 -1
- package/dist/connector/index.d.ts +1 -1
- package/dist/connector/index.d.ts.map +1 -1
- package/dist/connector/index.js +1 -1
- package/dist/connector/index.js.map +1 -1
- package/dist/connector/mcp/client.d.ts +96 -0
- package/dist/connector/mcp/client.d.ts.map +1 -1
- package/dist/connector/mcp/client.js +488 -13
- package/dist/connector/mcp/client.js.map +1 -1
- package/dist/connector/mcp/index.d.ts +1 -0
- package/dist/connector/mcp/index.d.ts.map +1 -1
- package/dist/connector/mcp/index.js +1 -0
- package/dist/connector/mcp/index.js.map +1 -1
- package/dist/connector/mcp/tasks.d.ts +40 -0
- package/dist/connector/mcp/tasks.d.ts.map +1 -0
- package/dist/connector/mcp/tasks.js +55 -0
- package/dist/connector/mcp/tasks.js.map +1 -0
- package/dist/constants/data-epoch.d.ts +18 -0
- package/dist/constants/data-epoch.d.ts.map +1 -0
- package/dist/constants/data-epoch.js +18 -0
- package/dist/constants/data-epoch.js.map +1 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +1 -0
- package/dist/constants/index.js.map +1 -1
- package/dist/constants/mcp/index.d.ts +1 -1
- package/dist/constants/mcp/index.d.ts.map +1 -1
- package/dist/constants/mcp/index.js +5 -0
- package/dist/constants/mcp/index.js.map +1 -1
- package/dist/constants/telemetry/index.d.ts +12 -0
- package/dist/constants/telemetry/index.d.ts.map +1 -1
- package/dist/constants/telemetry/index.js +12 -0
- package/dist/constants/telemetry/index.js.map +1 -1
- package/dist/eval/bootstrap.d.ts +122 -0
- package/dist/eval/bootstrap.d.ts.map +1 -0
- package/dist/eval/bootstrap.js +227 -0
- package/dist/eval/bootstrap.js.map +1 -0
- package/dist/eval/compare.d.ts +109 -0
- package/dist/eval/compare.d.ts.map +1 -0
- package/dist/eval/compare.js +324 -0
- package/dist/eval/compare.js.map +1 -0
- package/dist/eval/disclosure.d.ts +70 -0
- package/dist/eval/disclosure.d.ts.map +1 -0
- package/dist/eval/disclosure.js +64 -0
- package/dist/eval/disclosure.js.map +1 -0
- package/dist/eval/experiment.d.ts +23 -0
- package/dist/eval/experiment.d.ts.map +1 -1
- package/dist/eval/experiment.js +87 -15
- package/dist/eval/experiment.js.map +1 -1
- package/dist/eval/from-turn.d.ts.map +1 -1
- package/dist/eval/from-turn.js +5 -0
- package/dist/eval/from-turn.js.map +1 -1
- package/dist/eval/index.d.ts +13 -2
- package/dist/eval/index.d.ts.map +1 -1
- package/dist/eval/index.js +6 -1
- package/dist/eval/index.js.map +1 -1
- package/dist/eval/report-stats.d.ts +77 -0
- package/dist/eval/report-stats.d.ts.map +1 -0
- package/dist/eval/report-stats.js +184 -0
- package/dist/eval/report-stats.js.map +1 -0
- package/dist/eval/run-options.d.ts +30 -0
- package/dist/eval/run-options.d.ts.map +1 -0
- package/dist/eval/run-options.js +20 -0
- package/dist/eval/run-options.js.map +1 -0
- package/dist/eval/run-summary.d.ts +36 -0
- package/dist/eval/run-summary.d.ts.map +1 -0
- package/dist/eval/run-summary.js +57 -0
- package/dist/eval/run-summary.js.map +1 -0
- package/dist/eval/scorers.d.ts +32 -0
- package/dist/eval/scorers.d.ts.map +1 -1
- package/dist/eval/scorers.js +77 -0
- package/dist/eval/scorers.js.map +1 -1
- package/dist/eval/types.d.ts +74 -0
- package/dist/eval/types.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.d.ts +3 -0
- package/dist/manager/session/turn-recorder.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.js +18 -2
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1 -1
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/loader.d.ts +11 -0
- package/dist/plugin/loader.d.ts.map +1 -1
- package/dist/plugin/loader.js +15 -2
- package/dist/plugin/loader.js.map +1 -1
- package/dist/public-runtime.d.ts +10 -6
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +13 -5
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +2 -0
- package/dist/public-types.d.ts.map +1 -1
- package/dist/registry/command/kernel-commands.d.ts +3 -0
- package/dist/registry/command/kernel-commands.d.ts.map +1 -1
- package/dist/registry/command/kernel-commands.js +76 -5
- package/dist/registry/command/kernel-commands.js.map +1 -1
- package/dist/runtime/query/cancelled-before-start.d.ts.map +1 -1
- package/dist/runtime/query/cancelled-before-start.js +2 -0
- package/dist/runtime/query/cancelled-before-start.js.map +1 -1
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +17 -0
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +18 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +80 -1
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +18 -1
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +5 -0
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/result.d.ts.map +1 -1
- package/dist/runtime/query/result.js +2 -0
- package/dist/runtime/query/result.js.map +1 -1
- package/dist/runtime/query/turn/answer-claims.d.ts +52 -0
- package/dist/runtime/query/turn/answer-claims.d.ts.map +1 -0
- package/dist/runtime/query/turn/answer-claims.js +348 -0
- package/dist/runtime/query/turn/answer-claims.js.map +1 -0
- package/dist/runtime/query/turn/outcome-ledger.d.ts +7 -0
- package/dist/runtime/query/turn/outcome-ledger.d.ts.map +1 -1
- package/dist/runtime/query/turn/outcome-ledger.js +13 -0
- package/dist/runtime/query/turn/outcome-ledger.js.map +1 -1
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +1 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/loader.d.ts +15 -1
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +17 -3
- package/dist/skills/loader.js.map +1 -1
- package/dist/store/goal/index.d.ts +3 -1
- package/dist/store/goal/index.d.ts.map +1 -1
- package/dist/store/goal/index.js +6 -0
- package/dist/store/goal/index.js.map +1 -1
- package/dist/telemetry/genai.d.ts +98 -0
- package/dist/telemetry/genai.d.ts.map +1 -0
- package/dist/telemetry/genai.js +189 -0
- package/dist/telemetry/genai.js.map +1 -0
- package/dist/telemetry/runtime-accessors.d.ts +19 -0
- package/dist/telemetry/runtime-accessors.d.ts.map +1 -1
- package/dist/telemetry/runtime-accessors.js +43 -1
- package/dist/telemetry/runtime-accessors.js.map +1 -1
- package/dist/tools/goal/confirmation.d.ts +154 -0
- package/dist/tools/goal/confirmation.d.ts.map +1 -0
- package/dist/tools/goal/confirmation.js +290 -0
- package/dist/tools/goal/confirmation.js.map +1 -0
- package/dist/tools/goal/index.d.ts +4 -1
- package/dist/tools/goal/index.d.ts.map +1 -1
- package/dist/tools/goal/index.js +27 -2
- package/dist/tools/goal/index.js.map +1 -1
- package/dist/tools/hand-back/index.d.ts +48 -0
- package/dist/tools/hand-back/index.d.ts.map +1 -0
- package/dist/tools/hand-back/index.js +129 -0
- package/dist/tools/hand-back/index.js.map +1 -0
- package/dist/tools/schedules/schedule-tool.d.ts.map +1 -1
- package/dist/tools/schedules/schedule-tool.js +15 -1
- package/dist/tools/schedules/schedule-tool.js.map +1 -1
- package/dist/tools/schedules/types.d.ts +10 -0
- package/dist/tools/schedules/types.d.ts.map +1 -1
- package/dist/toolsets/manager.d.ts.map +1 -1
- package/dist/toolsets/manager.js +12 -0
- package/dist/toolsets/manager.js.map +1 -1
- package/dist/types/acp/index.d.ts +18 -0
- package/dist/types/acp/index.d.ts.map +1 -1
- package/dist/types/connector/mcp.d.ts +18 -0
- package/dist/types/connector/mcp.d.ts.map +1 -1
- package/dist/types/goal/index.d.ts +12 -0
- package/dist/types/goal/index.d.ts.map +1 -1
- package/dist/types/session/config.d.ts +8 -0
- package/dist/types/session/config.d.ts.map +1 -1
- package/dist/types/session/hand-back.d.ts +29 -0
- package/dist/types/session/hand-back.d.ts.map +1 -0
- package/dist/types/session/hand-back.js +11 -0
- package/dist/types/session/hand-back.js.map +1 -0
- package/dist/types/session/outcome.d.ts +37 -0
- package/dist/types/session/outcome.d.ts.map +1 -1
- package/dist/types/session/outcome.js.map +1 -1
- package/dist/types/session/turn.d.ts +19 -1
- package/dist/types/session/turn.d.ts.map +1 -1
- package/dist/types/session/turn.js.map +1 -1
- package/package.json +1 -1
- package/src/agents/file-definitions.ts +21 -0
- package/src/agents/index.ts +1 -0
- package/src/bridge/acp/server.ts +20 -0
- package/src/connector/index.ts +1 -0
- package/src/connector/mcp/client.ts +531 -14
- package/src/connector/mcp/index.ts +1 -0
- package/src/connector/mcp/tasks.ts +81 -0
- package/src/constants/data-epoch.ts +17 -0
- package/src/constants/index.ts +1 -0
- package/src/constants/mcp/index.ts +6 -1
- package/src/constants/telemetry/index.ts +13 -0
- package/src/eval/bootstrap.ts +314 -0
- package/src/eval/compare.ts +473 -0
- package/src/eval/disclosure.ts +136 -0
- package/src/eval/experiment.ts +127 -15
- package/src/eval/from-turn.ts +5 -0
- package/src/eval/index.ts +51 -0
- package/src/eval/report-stats.ts +257 -0
- package/src/eval/run-options.ts +49 -0
- package/src/eval/run-summary.ts +104 -0
- package/src/eval/scorers.ts +83 -0
- package/src/eval/types.ts +76 -0
- package/src/manager/session/turn-recorder.ts +23 -2
- package/src/plugin/index.ts +7 -1
- package/src/plugin/loader.ts +16 -2
- package/src/public-runtime.ts +70 -0
- package/src/public-types.ts +9 -0
- package/src/registry/command/kernel-commands.ts +94 -6
- package/src/runtime/query/cancelled-before-start.ts +2 -0
- package/src/runtime/query/index.ts +20 -0
- package/src/runtime/query/iteration/index.ts +85 -1
- package/src/runtime/query/iteration/phases/tool-review.ts +24 -1
- package/src/runtime/query/iteration/stream-turn.ts +10 -0
- package/src/runtime/query/result.ts +2 -0
- package/src/runtime/query/turn/answer-claims.ts +413 -0
- package/src/runtime/query/turn/outcome-ledger.ts +12 -0
- package/src/skills/index.ts +1 -0
- package/src/skills/loader.ts +21 -3
- package/src/store/goal/index.ts +11 -0
- package/src/telemetry/genai.ts +202 -0
- package/src/telemetry/runtime-accessors.ts +40 -1
- package/src/tools/goal/confirmation.ts +375 -0
- package/src/tools/goal/index.ts +37 -1
- package/src/tools/hand-back/index.ts +150 -0
- package/src/tools/schedules/schedule-tool.ts +19 -1
- package/src/tools/schedules/types.ts +10 -0
- package/src/toolsets/manager.ts +22 -0
- package/src/types/acp/index.ts +20 -0
- package/src/types/connector/mcp.ts +18 -0
- package/src/types/goal/index.ts +13 -0
- package/src/types/session/config.ts +8 -0
- package/src/types/session/hand-back.ts +31 -0
- package/src/types/session/outcome.ts +42 -0
- package/src/types/session/turn.ts +19 -1
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare what an answer claims about checks with what the turn's outcome
|
|
3
|
+
* record holds.
|
|
4
|
+
*
|
|
5
|
+
* A pure function: no model call, no clock, no I/O. The model's words only
|
|
6
|
+
* select which statement to check; the statement the kernel then makes is
|
|
7
|
+
* about the record, so it never claims more than the record establishes.
|
|
8
|
+
*
|
|
9
|
+
* It is built to stay silent. A notice that is wrong teaches people to ignore
|
|
10
|
+
* the line, so every doubt resolves to nothing: a negation, a hedge, a
|
|
11
|
+
* condition, a question, a quotation, code, a report of what someone else
|
|
12
|
+
* said, a statement about the past, a qualified claim ("all but one"), and any
|
|
13
|
+
* language but English. The claim wording and the words that cancel it are data
|
|
14
|
+
* below, each with the reason it is there.
|
|
15
|
+
*
|
|
16
|
+
* It looks at the record of THIS turn only. A turn whose record says nothing
|
|
17
|
+
* changed is never reported: "the tests pass" may be true of an earlier turn.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type {
|
|
21
|
+
AnswerClaimKind,
|
|
22
|
+
AnswerClaimMismatch,
|
|
23
|
+
AnswerClaimRecord,
|
|
24
|
+
TurnOutcome,
|
|
25
|
+
} from '../../../types/session/outcome.js'
|
|
26
|
+
import { auditCommandText } from './completion-audit.js'
|
|
27
|
+
|
|
28
|
+
/** Answers longer than this are read only up to it, so cost stays bounded. */
|
|
29
|
+
export const ANSWER_CLAIM_MAX_CHARS = 100_000
|
|
30
|
+
|
|
31
|
+
/** The longest quotation of the answer a mismatch carries, in characters. */
|
|
32
|
+
export const ANSWER_CLAIM_MAX_QUOTE_CHARS = 160
|
|
33
|
+
|
|
34
|
+
/** Words that may sit between a subject and its verb without changing the claim. */
|
|
35
|
+
const FILLER =
|
|
36
|
+
'(?:\\s+(?:all|now|also|still|again|both|are|were|is|have|has|been|currently|locally|fully))*'
|
|
37
|
+
|
|
38
|
+
/** Verbs that say a check came out well. */
|
|
39
|
+
const PASSED = '(?:pass(?:es|ed|ing)?|green|succeed(?:s|ed)?)\\b'
|
|
40
|
+
|
|
41
|
+
/** Determiners and kinds of test that may precede `tests`. */
|
|
42
|
+
const TEST_WORDS =
|
|
43
|
+
'(?:(?:all(?:\\s+of)?|the|my|our)\\s+)?(?:(?:unit|integration|e2e|existing|new|relevant|related|automated)\\s+)*'
|
|
44
|
+
|
|
45
|
+
interface ClaimPattern {
|
|
46
|
+
readonly kind: AnswerClaimKind
|
|
47
|
+
/** The claim in the kernel's words. */
|
|
48
|
+
readonly said: string
|
|
49
|
+
/** Matches the claim inside one clause. */
|
|
50
|
+
readonly re: RegExp
|
|
51
|
+
/** Why this wording, and why it is safe. */
|
|
52
|
+
readonly why: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Plain positive claims only. Each pattern needs the subject, at most a few
|
|
57
|
+
* filler words, and the verb, in that order, so a negation or a modal between
|
|
58
|
+
* them ("did not", "should") breaks the match by itself.
|
|
59
|
+
*/
|
|
60
|
+
export const ANSWER_CLAIM_PATTERNS: readonly ClaimPattern[] = [
|
|
61
|
+
{
|
|
62
|
+
kind: 'tests',
|
|
63
|
+
said: 'the tests pass',
|
|
64
|
+
re: new RegExp(`\\b${TEST_WORDS}tests\\b${FILLER}\\s+${PASSED}`, 'i'),
|
|
65
|
+
why: 'The plural "tests" as the subject. The singular is left out: "the test now passes" is usually about the one test just fixed, which says nothing about the run that failed.',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
kind: 'tests',
|
|
69
|
+
said: 'the test suite passes',
|
|
70
|
+
re: new RegExp(
|
|
71
|
+
`\\b(?:test\\s+suite|the\\s+suite|(?:whole|full|entire)\\s+(?:test\\s+)?suite|test\\s+run)\\b${FILLER}\\s+${PASSED}`,
|
|
72
|
+
'i',
|
|
73
|
+
),
|
|
74
|
+
why: 'The suite as a whole is the same claim as "the tests pass", also as "the whole suite". A bare "suite" is not enough: it also names a benchmark suite or a suite of rooms.',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
kind: 'tests',
|
|
78
|
+
said: 'no test failures',
|
|
79
|
+
re: /\b(?:no|zero|0)\s+(?:test\s+failures|failing\s+tests|failed\s+tests|test\s+errors)\b/i,
|
|
80
|
+
why: 'The same claim said by counting failures. It is matched whole, so the word "no" in it is not read as a negation.',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
kind: 'tests',
|
|
84
|
+
said: 'no test failures',
|
|
85
|
+
re: /\b(?:no\s+tests?\s+(?:are\s+|is\s+)?(?:fail(?:s|ing|ed)?)|tests?\s+(?:are\s+not|aren['\u2019]t|do\s+not|don['\u2019]t|did\s+not|didn['\u2019]t)\s+(?:fail|failing))\b/i,
|
|
86
|
+
why: 'The same claim said as a negated failure: "no tests are failing", "the tests do not fail". The pattern holds its own negation, and the failure word in it is not read as a failure.',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
kind: 'tests',
|
|
90
|
+
said: 'the tests pass',
|
|
91
|
+
re: /\b(?:ran|run|running)\s+(?:(?:all\s+)?(?:of\s+)?(?:the|my|our)\s+)?(?:\w+\s+){0,2}(?:tests|test\s+suite|suite)\s+and\s+(?:they|it)\s+(?:all\s+)?(?:pass(?:es|ed)?|are\s+(?:green|passing))\b/i,
|
|
92
|
+
why: 'The pronoun form, the commonest plain phrasing: "I ran the tests and they all pass". The run and its result are in one clause, and a negation between them ("they did not pass") breaks the match.',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
kind: 'checks',
|
|
96
|
+
said: 'the checks pass',
|
|
97
|
+
re: /^\W*(?:it['\u2019]?s\s+|it\s+is\s+|everything\s+is\s+)?all\s+green\b/i,
|
|
98
|
+
why: 'The shortest summary: "All green." Anchored to the start of the sentence, so "not all green" never matches.',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
kind: 'build',
|
|
102
|
+
said: 'the build succeeds',
|
|
103
|
+
re: /\b(?:it|everything)\s+compiles\b/i,
|
|
104
|
+
why: 'A bare "it compiles". The pronoun or "everything" is required: "the compiles list" and similar nouns are not claims.',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
kind: 'build',
|
|
108
|
+
said: 'the build succeeds',
|
|
109
|
+
re: new RegExp(
|
|
110
|
+
`\\b(?:build|compilation|compile)\\b${FILLER}\\s+(?:succeed(?:s|ed)?|pass(?:es|ed)?|green|completes?|completed|works|worked|is\\s+clean)\\b`,
|
|
111
|
+
'i',
|
|
112
|
+
),
|
|
113
|
+
why: 'The build as the subject with a success verb. "The build" alone, as in "the build script", never matches because the verb must follow.',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
kind: 'build',
|
|
117
|
+
said: 'the build succeeds',
|
|
118
|
+
re: /\b(?:builds?|compiles?)\s+(?:cleanly|successfully|fine)\b/i,
|
|
119
|
+
why: 'The adverb form: "it builds cleanly", "everything compiles successfully".',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
kind: 'lint',
|
|
123
|
+
said: 'lint is clean',
|
|
124
|
+
re: new RegExp(
|
|
125
|
+
`\\blint(?:ing|er)?\\b${FILLER}\\s+(?:is\\s+|are\\s+)?(?:clean|pass(?:es|ed|ing)?|green|succeed(?:s|ed)?|ok)\\b`,
|
|
126
|
+
'i',
|
|
127
|
+
),
|
|
128
|
+
why: 'Lint as the subject with a clean or passing verb.',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
kind: 'lint',
|
|
132
|
+
said: 'lint is clean',
|
|
133
|
+
re: /\bno\s+lint(?:ing)?\s+(?:errors?|warnings?|issues?|problems?)\b/i,
|
|
134
|
+
why: 'The same claim by counting: "no lint errors". Matched whole, so its own "no" is not a negation.',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
kind: 'typecheck',
|
|
138
|
+
said: 'type checking passes',
|
|
139
|
+
re: new RegExp(
|
|
140
|
+
`\\b(?:type[\\s-]?check(?:ing|s)?|typecheck|tsc)\\b${FILLER}\\s+(?:is\\s+|are\\s+)?(?:clean|pass(?:es|ed|ing)?|green|succeed(?:s|ed)?|ok)\\b`,
|
|
141
|
+
'i',
|
|
142
|
+
),
|
|
143
|
+
why: 'Type checking, the one-word spelling and the compiler named for it, as the subject. "Type checker" is left out: "the type checker passes the argument on" names what a checker does, not a result.',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
kind: 'typecheck',
|
|
147
|
+
said: 'type checking passes',
|
|
148
|
+
re: /\bno\s+type\s+errors\b/i,
|
|
149
|
+
why: 'The same claim by counting: "no type errors".',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
kind: 'checks',
|
|
153
|
+
said: 'the checks pass',
|
|
154
|
+
re: new RegExp(
|
|
155
|
+
`\\b(?:all\\s+(?:of\\s+)?(?:the\\s+)?checks|the\\s+(?:ci\\s+)?checks|ci\\s+checks|everything)\\b${FILLER}\\s+${PASSED}`,
|
|
156
|
+
'i',
|
|
157
|
+
),
|
|
158
|
+
why: 'The catch-all a summary reaches for: "all checks pass", "the CI checks pass", "everything is green".',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
kind: 'tests',
|
|
162
|
+
said: 'the tests pass',
|
|
163
|
+
re: /^\W*(?:\*\*)?(?:tests?|test\s+suite)(?:\*\*)?\s*[:\-\u2013]\s*(?:\*\*)?\s*(?:all\s+)?(?:\d+\s+)?(?:pass(?:es|ed|ing)?|green|success(?:ful)?|ok|\u2705|\u2714)/i,
|
|
164
|
+
why: 'The label form of a summary list: "Tests: passing". Anchored to the start of the line, where a label stands.',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
kind: 'build',
|
|
168
|
+
said: 'the build succeeds',
|
|
169
|
+
re: /^\W*(?:\*\*)?build(?:\*\*)?\s*[:\-–]\s*(?:\*\*)?\s*(?:pass(?:es|ed|ing)?|green|success(?:ful)?|ok|clean|✅|✔)/i,
|
|
170
|
+
why: 'The label form: "Build: success".',
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
kind: 'lint',
|
|
174
|
+
said: 'lint is clean',
|
|
175
|
+
re: /^\W*(?:\*\*)?lint(?:ing)?(?:\*\*)?\s*[:\-–]\s*(?:\*\*)?\s*(?:pass(?:es|ed|ing)?|green|success(?:ful)?|ok|clean|✅|✔)/i,
|
|
176
|
+
why: 'The label form: "Lint: clean".',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
kind: 'typecheck',
|
|
180
|
+
said: 'type checking passes',
|
|
181
|
+
re: /^\W*(?:\*\*)?(?:type[\s-]?check(?:ing)?|typecheck)(?:\*\*)?\s*[:\-–]\s*(?:\*\*)?\s*(?:pass(?:es|ed|ing)?|green|success(?:ful)?|ok|clean|✅|✔)/i,
|
|
182
|
+
why: 'The label form: "Typecheck: passing".',
|
|
183
|
+
},
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
/** Words that turn the claim around. Looked for in the few words before it. */
|
|
187
|
+
const NEGATION =
|
|
188
|
+
/\b(?:not|no|never|none|nothing|nobody|zero|scarcely|neither|nor|cannot|unable|without|barely|hardly|n't|isn|aren|doesn|don|didn|wasn|weren|won|couldn|can't|won't)\b|n['’]t\b/i
|
|
189
|
+
|
|
190
|
+
/** Words that make it a wish, a plan, a condition, a request or a guess. Looked for in the few words before it. */
|
|
191
|
+
const HEDGE =
|
|
192
|
+
/\b(?:if|once|when|whenever|unless|until|assuming|provided|whether|in\s+case|so\s+that|ensure|ensures|ensuring|make\s+sure|makes\s+sure|make|makes|get|gets|keep|keeps|verify|verifying|confirm|confirming|check\s+(?:that|whether)|hope|hopes|hoping|hopefully|expect|expects|expecting|expected|want|wants|need|needs|must|should|would|could|might|may|can|will|probably|likely|maybe|perhaps|presumably|supposedly|apparently|ideally|seems?|appears?|think|believe|assume|guess|suppose|imagine|wonder|let|try|please|goal|aim|aims|target)\b|'ll\b/i
|
|
193
|
+
|
|
194
|
+
/** Words that report what someone or something else said. Looked for in the whole clause. */
|
|
195
|
+
const REPORTING =
|
|
196
|
+
/\b(?:said|says|say|saying|claims?|claimed|claiming|reports?|reported|reporting|according\s+to|states?|stated|mentions?|mentioned|noted|told|tells|asked|asks|promises?|promised|advertises?|advertised)\b/i
|
|
197
|
+
|
|
198
|
+
/** Words that put the claim in the past, before this turn's change. Looked for in the whole clause. */
|
|
199
|
+
const EARLIER =
|
|
200
|
+
/\b(?:before|earlier|previously|prior|originally|beforehand|initially|yesterday|formerly|used\s+to|at\s+first|last\s+time|last\s+week|until\s+now|in\s+the\s+past|back\s+then|had\s+been)\b/i
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Words that make the claim partial, a guess or someone else's. Looked for in
|
|
204
|
+
* the few words after it. The last few are the plain negation of other languages
|
|
205
|
+
* ("değil", "nicht", "pas") for a clause that holds an English claim.
|
|
206
|
+
*/
|
|
207
|
+
const QUALIFIER =
|
|
208
|
+
/\b(?:except|besides|apart|aside|other\s+than|but\s+for|barring|mostly|partly|partially|mainly|almost|nearly|only|if|unless|provided|once|when|until|assuming|so\s+far\s+as|aside\s+from|on\s+ci|in\s+theory|apparently|supposedly|allegedly|presumably|seemingly|reportedly|hopefully|probably|maybe|perhaps|goal|aim|target|de[g\u011f]il|nicht|nein|pas)\b/i
|
|
209
|
+
|
|
210
|
+
/** A failure named anywhere else in the sentence: the sentence is reporting one, not claiming a pass. */
|
|
211
|
+
const FAILURE =
|
|
212
|
+
/\b(?:fail|fails|failed|failing|failure|failures|error|errors|broken|breaks|red|regress(?:ed|es|ion)?)\b/i
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A word anywhere in the sentence that makes every claim in it a guess, or a
|
|
216
|
+
* claim in another language's negation. It sits at sentence level because a
|
|
217
|
+
* comma often separates it from the claim: "The tests pass, apparently."
|
|
218
|
+
*/
|
|
219
|
+
const SENTENCE_HEDGE =
|
|
220
|
+
/\b(?:apparently|supposedly|allegedly|presumably|seemingly|reportedly|hopefully|probably|maybe|perhaps|de[g\u011f]il|nicht|nein)\b/i
|
|
221
|
+
|
|
222
|
+
/** Words a sentence may start with that make everything after it conditional. */
|
|
223
|
+
const CONDITIONAL_START =
|
|
224
|
+
/^\W*(?:if|once|when|whenever|unless|assuming|provided|in\s+case|should|were|had)\b/i
|
|
225
|
+
|
|
226
|
+
/** How many words before and after a claim are read for what cancels it. */
|
|
227
|
+
const WORDS_BEFORE = 6
|
|
228
|
+
const WORDS_AFTER = 6
|
|
229
|
+
|
|
230
|
+
/** What a verb that takes an object starts with: "the type checker passes the argument on" is not a result. */
|
|
231
|
+
const OBJECT_AFTER =
|
|
232
|
+
/^\W*(?:the|a|an|its|their|his|her|them|it|this|that|these|those|each|every|any|some|through|along)\b/i
|
|
233
|
+
|
|
234
|
+
/** A claim worded as a subject ("tests passing is the goal"): the verb is a gerund that a copula follows. */
|
|
235
|
+
const COPULA_AFTER = /^\W*(?:is|are|was|were|means|requires|needs)\b/i
|
|
236
|
+
|
|
237
|
+
/** What is left of an answer once the places a claim does not count are taken out. */
|
|
238
|
+
function readableText(answer: string): string {
|
|
239
|
+
return (
|
|
240
|
+
answer
|
|
241
|
+
.slice(0, ANSWER_CLAIM_MAX_CHARS)
|
|
242
|
+
// A fenced block, or one the answer never closed.
|
|
243
|
+
.replace(/(```|~~~)[\s\S]*?(?:\1|$)/g, ' _ ')
|
|
244
|
+
// A block quotation: its lines are somebody else's words.
|
|
245
|
+
.replace(/^[ \t]*>.*$/gm, ' ')
|
|
246
|
+
// An inline code span names a command or a symbol, not a claim.
|
|
247
|
+
.replace(/`[^`\n]{0,400}`/g, ' _ ')
|
|
248
|
+
// A quotation in either kind of double quotes.
|
|
249
|
+
.replace(/"[^"\n]{0,400}"/g, ' _ ')
|
|
250
|
+
.replace(/“[^”\n]{0,400}”/g, ' _ ')
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Sentences, and within one the clauses a claim and its hedge do not cross. */
|
|
255
|
+
function clausesOf(text: string): { sentenceEndsWithQuestion: boolean; clauses: string[] }[] {
|
|
256
|
+
const out: { sentenceEndsWithQuestion: boolean; clauses: string[] }[] = []
|
|
257
|
+
// A semicolon ends a statement, so a failure named in one half does not cancel a claim in the other.
|
|
258
|
+
for (const raw of text.split(/(?<=[.!?])\s+|\n+|;\s*/)) {
|
|
259
|
+
const sentence = raw.trim()
|
|
260
|
+
if (sentence === '') continue
|
|
261
|
+
if (CONDITIONAL_START.test(sentence) || SENTENCE_HEDGE.test(sentence)) continue
|
|
262
|
+
// What the claim patterns themselves match ("no test failures") is not a failure.
|
|
263
|
+
let rest = sentence
|
|
264
|
+
for (const pattern of ANSWER_CLAIM_PATTERNS) {
|
|
265
|
+
rest = rest.replace(new RegExp(pattern.re.source, 'gi'), ' ')
|
|
266
|
+
}
|
|
267
|
+
// "no errors", "without failures": a count of none is not a failure.
|
|
268
|
+
rest = rest.replace(/\b(?:no|zero|0|without)\s+(?:\w+\s+)?(?:errors?|failures?)\b/gi, ' ')
|
|
269
|
+
if (FAILURE.test(rest)) continue
|
|
270
|
+
out.push({
|
|
271
|
+
sentenceEndsWithQuestion: /\?\W*$/.test(sentence),
|
|
272
|
+
// A comma, a semicolon, a dash, a bracket, or a word that turns the sentence around.
|
|
273
|
+
clauses: sentence.split(
|
|
274
|
+
/[,;()—]|\s-\s|\s–\s|\b(?:but|however|although|though|while|whereas|yet|because|since|so)\b/i,
|
|
275
|
+
),
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
return out
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function wordsBefore(clause: string, at: number): string {
|
|
282
|
+
return clause.slice(0, at).trim().split(/\s+/).slice(-WORDS_BEFORE).join(' ')
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function wordsAfter(clause: string, to: number): string {
|
|
286
|
+
return clause.slice(to).trim().split(/\s+/).slice(0, WORDS_AFTER).join(' ')
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** The claim in this clause, when it is a plain, present, positive one. */
|
|
290
|
+
function claimIn(clause: string): ClaimPattern | undefined {
|
|
291
|
+
if (REPORTING.test(clause) || EARLIER.test(clause)) return undefined
|
|
292
|
+
for (const pattern of ANSWER_CLAIM_PATTERNS) {
|
|
293
|
+
const found = pattern.re.exec(clause)
|
|
294
|
+
if (!found) continue
|
|
295
|
+
const before = wordsBefore(clause, found.index)
|
|
296
|
+
if (NEGATION.test(before) || HEDGE.test(before)) continue
|
|
297
|
+
const after = wordsAfter(clause, found.index + found[0].length)
|
|
298
|
+
if (QUALIFIER.test(after)) continue
|
|
299
|
+
if (/\bpass(?:es)?$/i.test(found[0]) && OBJECT_AFTER.test(after)) continue
|
|
300
|
+
if (/\bpassing$/i.test(found[0]) && COPULA_AFTER.test(after)) continue
|
|
301
|
+
if (/\d+\s+of\s+\d+/.test(clause)) continue
|
|
302
|
+
return pattern
|
|
303
|
+
}
|
|
304
|
+
return undefined
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** The kind of check a command line is, when it says so. */
|
|
308
|
+
const COMMAND_KINDS: readonly { kind: AnswerClaimKind; re: RegExp }[] = [
|
|
309
|
+
{
|
|
310
|
+
kind: 'lint',
|
|
311
|
+
re: /\b(?:lint|eslint|biome|prettier|ruff|flake8|rubocop|clippy|golangci-lint)\b/i,
|
|
312
|
+
},
|
|
313
|
+
{ kind: 'typecheck', re: /\b(?:typecheck|tsc|mypy|pyright)\b/i },
|
|
314
|
+
{ kind: 'tests', re: /\b(?:test|tests|vitest|jest|pytest|mocha|phpunit|spec)\b/i },
|
|
315
|
+
{ kind: 'build', re: /\b(?:build|compile|bundle)\b/i },
|
|
316
|
+
]
|
|
317
|
+
|
|
318
|
+
/** A type check is a compile, so a claim about either is about both. */
|
|
319
|
+
function sameKind(a: AnswerClaimKind, b: AnswerClaimKind): boolean {
|
|
320
|
+
if (a === b) return true
|
|
321
|
+
return (a === 'build' && b === 'typecheck') || (a === 'typecheck' && b === 'build')
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Every kind a command line is: a chained `pnpm lint && pnpm test` is both. */
|
|
325
|
+
function kindsOfCommand(command: string): AnswerClaimKind[] {
|
|
326
|
+
return COMMAND_KINDS.filter((k) => k.re.test(command)).map((k) => k.kind)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** The failed check the claim is about, or `undefined` when every failed check is plainly another kind. */
|
|
330
|
+
function failedCheckFor(
|
|
331
|
+
record: TurnOutcome,
|
|
332
|
+
claim: AnswerClaimKind,
|
|
333
|
+
): { command?: string } | undefined {
|
|
334
|
+
const checks = Array.isArray(record.checks) ? record.checks : []
|
|
335
|
+
const failed = checks.filter((c) => c?.result === 'failed')
|
|
336
|
+
if (failed.length === 0) return {}
|
|
337
|
+
const related = failed.filter((c) => {
|
|
338
|
+
const kinds = kindsOfCommand(typeof c.command === 'string' ? c.command : '')
|
|
339
|
+
return claim === 'checks' || kinds.length === 0 || kinds.some((kind) => sameKind(kind, claim))
|
|
340
|
+
})
|
|
341
|
+
if (related.length === 0) return undefined
|
|
342
|
+
const only = related.length === 1 ? related[0]?.command : undefined
|
|
343
|
+
return typeof only === 'string' ? { command: only } : {}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* The one mismatch between a plain claim in `answer` and this turn's `outcome`,
|
|
348
|
+
* or `undefined`.
|
|
349
|
+
*
|
|
350
|
+
* Reports only when the record says a check failed, or says files changed and
|
|
351
|
+
* no check ran or none established a pass. Never when the record says nothing
|
|
352
|
+
* changed, that the checks passed, or that it cannot tell: in those the claim
|
|
353
|
+
* may be true. English only: a claim in another language is not found.
|
|
354
|
+
*/
|
|
355
|
+
export function compareAnswerToOutcome(
|
|
356
|
+
answer: string | undefined | null,
|
|
357
|
+
outcome: TurnOutcome | undefined,
|
|
358
|
+
): AnswerClaimMismatch | undefined {
|
|
359
|
+
// A notice is never worth failing a turn for: input that does not have the
|
|
360
|
+
// expected shape (a hook-supplied record) is read as "nothing to say".
|
|
361
|
+
try {
|
|
362
|
+
return compare(answer, outcome)
|
|
363
|
+
} catch {
|
|
364
|
+
return undefined
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function compare(
|
|
369
|
+
answer: string | undefined | null,
|
|
370
|
+
outcome: TurnOutcome | undefined,
|
|
371
|
+
): AnswerClaimMismatch | undefined {
|
|
372
|
+
if (typeof answer !== 'string' || answer === '' || !outcome) return undefined
|
|
373
|
+
const verdict = outcome.verdict
|
|
374
|
+
if (verdict !== 'failing' && verdict !== 'unchecked' && verdict !== 'inconclusive') {
|
|
375
|
+
return undefined
|
|
376
|
+
}
|
|
377
|
+
for (const { sentenceEndsWithQuestion, clauses } of clausesOf(readableText(answer))) {
|
|
378
|
+
if (sentenceEndsWithQuestion) continue
|
|
379
|
+
for (const clause of clauses) {
|
|
380
|
+
const pattern = claimIn(clause)
|
|
381
|
+
if (!pattern) continue
|
|
382
|
+
let record: AnswerClaimRecord
|
|
383
|
+
let recordSays: string
|
|
384
|
+
if (verdict === 'failing') {
|
|
385
|
+
const failed = failedCheckFor(outcome, pattern.kind)
|
|
386
|
+
if (!failed) continue
|
|
387
|
+
record = 'check-failed'
|
|
388
|
+
recordSays = failed.command
|
|
389
|
+
? `${auditCommandText(failed.command)} failed`
|
|
390
|
+
: 'a check failed'
|
|
391
|
+
} else if (verdict === 'unchecked') {
|
|
392
|
+
record = 'unchecked'
|
|
393
|
+
recordSays = 'files changed and no check ran'
|
|
394
|
+
} else {
|
|
395
|
+
record = 'unreadable'
|
|
396
|
+
recordSays = 'files changed and no check gave a result that could be read'
|
|
397
|
+
}
|
|
398
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: control characters are what this removes
|
|
399
|
+
const folded = clause.replace(/[\u0000-\u001f\u007f-\u009f\s]+/g, ' ').trim()
|
|
400
|
+
const quote =
|
|
401
|
+
folded.length <= ANSWER_CLAIM_MAX_QUOTE_CHARS
|
|
402
|
+
? folded
|
|
403
|
+
: `${folded.slice(0, ANSWER_CLAIM_MAX_QUOTE_CHARS - 3)}...`
|
|
404
|
+
return { claim: pattern.kind, said: pattern.said, quote, record, recordSays }
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return undefined
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/** The mismatch as one plain line, for a person: `The answer says the tests pass. The record: ...` */
|
|
411
|
+
export function describeAnswerClaimMismatch(mismatch: AnswerClaimMismatch): string {
|
|
412
|
+
return `The answer says ${mismatch.said}. The record: ${mismatch.recordSays}.`
|
|
413
|
+
}
|
|
@@ -539,6 +539,18 @@ export class OutcomeLedger {
|
|
|
539
539
|
return this.compute(null)
|
|
540
540
|
}
|
|
541
541
|
|
|
542
|
+
/**
|
|
543
|
+
* The record as it stands now, with the workspace asked once, and without
|
|
544
|
+
* settling: the turn may go on and `finish` still answers once at the end.
|
|
545
|
+
* Used by a host that wants the whole record before it lets a turn end. A
|
|
546
|
+
* record that is not whole is `undefined`, as in `finish`.
|
|
547
|
+
*/
|
|
548
|
+
async preview(): Promise<TurnOutcome | undefined> {
|
|
549
|
+
if (this.unrecorded) return undefined
|
|
550
|
+
if (this.settled !== undefined) return this.settled
|
|
551
|
+
return this.compute(this.started ? await this.detect() : null)
|
|
552
|
+
}
|
|
553
|
+
|
|
542
554
|
private compute(endFingerprint: string | null): TurnOutcome {
|
|
543
555
|
const workspace = this.started && this.startFingerprint !== null && endFingerprint !== null
|
|
544
556
|
const moved = workspace && this.startFingerprint !== endFingerprint
|
package/src/skills/index.ts
CHANGED
package/src/skills/loader.ts
CHANGED
|
@@ -257,6 +257,24 @@ function estimateTokens(text: string): number {
|
|
|
257
257
|
return Math.ceil(text.length / CHARS_PER_TOKEN)
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
+
/**
|
|
261
|
+
* A `SKILL.md`'s text to its metadata and body, or a thrown reason.
|
|
262
|
+
*
|
|
263
|
+
* The one place the skill rules are applied to text. `loadSkill` reads the
|
|
264
|
+
* file and calls this; a host that checks a skill without loading it (the
|
|
265
|
+
* plugin validator) reads the file its own way and calls the same function,
|
|
266
|
+
* so the two cannot disagree about what a valid skill is.
|
|
267
|
+
*
|
|
268
|
+
* @param dirPath The skill's folder: the name must match its last segment.
|
|
269
|
+
*/
|
|
270
|
+
export function parseSkillMarkdown(
|
|
271
|
+
raw: string,
|
|
272
|
+
dirPath: string,
|
|
273
|
+
): { readonly metadata: SkillMetadata; readonly body: string } {
|
|
274
|
+
const parsed = parseFrontmatter(raw, sourceLabel(dirPath), SKILL_FRONTMATTER_OPTIONS)
|
|
275
|
+
return { metadata: toSkillMetadata(parsed, dirPath), body: parsed.body }
|
|
276
|
+
}
|
|
277
|
+
|
|
260
278
|
export async function loadSkill(
|
|
261
279
|
dirPath: string,
|
|
262
280
|
level: SkillDisclosureLevel = 'metadata',
|
|
@@ -264,8 +282,8 @@ export async function loadSkill(
|
|
|
264
282
|
): Promise<SkillLoadResult> {
|
|
265
283
|
const skillMdPath = join(dirPath, SKILL_FILENAME)
|
|
266
284
|
const raw = await readFile(skillMdPath, 'utf-8')
|
|
267
|
-
const
|
|
268
|
-
const metadata =
|
|
285
|
+
const parsedSkill = parseSkillMarkdown(raw, dirPath)
|
|
286
|
+
const metadata = parsedSkill.metadata
|
|
269
287
|
|
|
270
288
|
const skill: Skill = {
|
|
271
289
|
metadata,
|
|
@@ -273,7 +291,7 @@ export async function loadSkill(
|
|
|
273
291
|
}
|
|
274
292
|
|
|
275
293
|
if (level === 'full' || level === 'assets') {
|
|
276
|
-
skill.body =
|
|
294
|
+
skill.body = parsedSkill.body
|
|
277
295
|
}
|
|
278
296
|
|
|
279
297
|
const metadataTokens = estimateTokens(`${metadata.name}: ${metadata.description}`)
|
package/src/store/goal/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { join } from 'node:path'
|
|
|
3
3
|
import { TenantIsolationError } from '../../session/errors.js'
|
|
4
4
|
import type {
|
|
5
5
|
GoalBlockReason,
|
|
6
|
+
GoalConfirmedBy,
|
|
6
7
|
GoalPhase,
|
|
7
8
|
GoalRef,
|
|
8
9
|
GoalRoundAuthority,
|
|
@@ -29,6 +30,8 @@ export interface CreateSessionGoalParams {
|
|
|
29
30
|
readonly sessionId: SessionId
|
|
30
31
|
readonly objective: string
|
|
31
32
|
readonly maxGoalRounds?: number
|
|
33
|
+
/** Set only when this goal chooses; absent leaves the choice to the host's setting. */
|
|
34
|
+
readonly confirmedBy?: GoalConfirmedBy
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
export interface EditSessionGoalParams {
|
|
@@ -273,6 +276,13 @@ abstract class SessionGoalStoreBase implements SessionGoalStore {
|
|
|
273
276
|
const sessionId = await this.requireSession(params.sessionId, tenantId)
|
|
274
277
|
const text = objective(params.objective)
|
|
275
278
|
const maxRounds = maxGoalRounds(params.maxGoalRounds)
|
|
279
|
+
if (
|
|
280
|
+
params.confirmedBy !== undefined &&
|
|
281
|
+
params.confirmedBy !== 'model' &&
|
|
282
|
+
params.confirmedBy !== 'record'
|
|
283
|
+
) {
|
|
284
|
+
throw new TypeError('goal confirmedBy must be "model" or "record".')
|
|
285
|
+
}
|
|
276
286
|
return await backendFor(this).transact(sessionId, (existing) => {
|
|
277
287
|
if (existing) {
|
|
278
288
|
assertTenant(existing, tenantId)
|
|
@@ -290,6 +300,7 @@ abstract class SessionGoalStoreBase implements SessionGoalStore {
|
|
|
290
300
|
phase: 'active',
|
|
291
301
|
maxGoalRounds: maxRounds,
|
|
292
302
|
roundsAdmitted: 0,
|
|
303
|
+
...(params.confirmedBy !== undefined ? { confirmedBy: params.confirmedBy } : {}),
|
|
293
304
|
createdAt: now,
|
|
294
305
|
updatedAt: now,
|
|
295
306
|
}
|