dialai 1.2.0 → 1.3.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/.claude/skills/dial-machine/references/api-reference.md +2 -1
- package/.claude/specs/enriched-transitions.md +1100 -0
- package/.claude/specs/proposal-metadata-update.md +211 -0
- package/.claude/specs/submit-proposal-cleanup.md +319 -0
- package/dist/dialai/api.d.ts.map +1 -1
- package/dist/dialai/api.js +28 -26
- package/dist/dialai/api.js.map +1 -1
- package/dist/dialai/types.d.ts +2 -0
- package/dist/dialai/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Strategy Result Operational Metrics
|
|
2
|
+
|
|
3
|
+
## Executive Summary
|
|
4
|
+
|
|
5
|
+
Extend `ProposerStrategyResult` with optional operational fields (`costUSD`, `latencyMsec`, `numInputTokens`, `numOutputTokens`) so that when `submitProposal` invokes a strategy internally, the resulting proposal carries the same cost and latency data it would carry if the caller had submitted explicitly.
|
|
6
|
+
|
|
7
|
+
## Objective
|
|
8
|
+
|
|
9
|
+
Close the data gap between the two `submitProposal` paths so both produce proposals with full operational metrics.
|
|
10
|
+
|
|
11
|
+
## In Scope
|
|
12
|
+
|
|
13
|
+
- Extend `ProposerStrategyResult` in `types.ts` with four optional fields
|
|
14
|
+
- Update `executeProposerLlm` in `llm.ts` to capture timing and token usage from `callLlm`
|
|
15
|
+
- Update `invokeProposerStrategy` in `api.ts` to use `ProposerStrategyResult` as its return type
|
|
16
|
+
- Update `submitProposal` in `api.ts` to merge strategy-returned metrics into the proposal
|
|
17
|
+
- Unit tests for the new behavior
|
|
18
|
+
- Verify all existing tests still pass
|
|
19
|
+
|
|
20
|
+
## Out of Scope
|
|
21
|
+
|
|
22
|
+
- Changing `callLlm`'s return type or signature
|
|
23
|
+
- Changing `ArbiterStrategyResult`
|
|
24
|
+
- Adding `costUSD` calculation to `executeProposerLlm`
|
|
25
|
+
- Webhook proposer timing (`executeProposerWebhook`)
|
|
26
|
+
- Changes to `executeContextWebhookProposer` (it delegates to `executeProposerLlm`; metrics cascade automatically)
|
|
27
|
+
|
|
28
|
+
## Assumptions and Constraints
|
|
29
|
+
|
|
30
|
+
- `callLlm` returns `{ content: string; usage?: { prompt_tokens?: number; completion_tokens?: number } }` — confirmed at `llm.ts:110-115`
|
|
31
|
+
- `executeProposerLlm` currently discards `usage` and does not measure wall-clock time
|
|
32
|
+
- `submitProposal` opts values (`costUSD`, `latencyMsec`, etc.) take precedence over strategy-returned values when both present
|
|
33
|
+
- `ProposerStrategyResult` is the return type for `strategyFn`, `executeProposerLlm`, `executeProposerWebhook`, and `executeContextWebhookProposer`
|
|
34
|
+
- Custom `strategyFn` implementations are not required to return the new fields (they are optional)
|
|
35
|
+
- `invokeProposerStrategy` is a private function in `api.ts` with explicit return type that must be updated
|
|
36
|
+
|
|
37
|
+
## Files to Modify
|
|
38
|
+
|
|
39
|
+
| File | Action |
|
|
40
|
+
|------|--------|
|
|
41
|
+
| `src/dialai/types.ts` | Add 4 optional fields to `ProposerStrategyResult` |
|
|
42
|
+
| `src/dialai/llm.ts` | Update `executeProposerLlm` to capture timing + usage |
|
|
43
|
+
| `src/dialai/api.ts` | Update `invokeProposerStrategy` return type; update `submitProposal` to merge metrics |
|
|
44
|
+
| `tests/unit/submit-proposal.test.ts` | Add 3 new tests |
|
|
45
|
+
| `src/dialai/llm.test.ts` | Add 1 new test |
|
|
46
|
+
|
|
47
|
+
## Files to Read (do not modify)
|
|
48
|
+
|
|
49
|
+
| File | Why |
|
|
50
|
+
|------|-----|
|
|
51
|
+
| `src/dialai/llm-audit.test.ts` | Mock patterns for `callLlm` |
|
|
52
|
+
| `src/dialai/store.ts` | Understand `Proposal` storage |
|
|
53
|
+
|
|
54
|
+
## Implementation Plan
|
|
55
|
+
|
|
56
|
+
### Phase 1: Extend the type
|
|
57
|
+
|
|
58
|
+
In `src/dialai/types.ts`, add four optional fields to `ProposerStrategyResult` (after `reasoning`):
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
export interface ProposerStrategyResult {
|
|
62
|
+
transitionName: string;
|
|
63
|
+
toState: string;
|
|
64
|
+
reasoning: string;
|
|
65
|
+
/** Cost in USD to produce this result, if known */
|
|
66
|
+
costUSD?: number;
|
|
67
|
+
/** Wall-clock time in milliseconds, if measured */
|
|
68
|
+
latencyMsec?: number;
|
|
69
|
+
/** Input tokens consumed, if known */
|
|
70
|
+
numInputTokens?: number;
|
|
71
|
+
/** Output tokens consumed, if known */
|
|
72
|
+
numOutputTokens?: number;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Validate:** Run `npm run typecheck`. This is backward-compatible; expect no errors.
|
|
77
|
+
|
|
78
|
+
### Phase 2: Update `executeProposerLlm`
|
|
79
|
+
|
|
80
|
+
In `src/dialai/llm.ts`, the function currently (lines 238-259) calls `callLlm`, parses the JSON, and returns the parsed result directly. Update it to:
|
|
81
|
+
|
|
82
|
+
1. Record `Date.now()` before calling `callLlm`
|
|
83
|
+
2. Compute `latencyMsec` after `callLlm` returns
|
|
84
|
+
3. Extract `usage.prompt_tokens` and `usage.completion_tokens` from the result
|
|
85
|
+
4. Return an explicit object with the three existing fields plus the three new measurement fields
|
|
86
|
+
|
|
87
|
+
The try/catch structure must be preserved. The `as ProposerStrategyResult` cast on the parsed JSON should be removed in favor of explicit field extraction (to avoid casting an object that lacks the metric fields into the type that now declares them).
|
|
88
|
+
|
|
89
|
+
**Validate:** Run `npm test`. All existing tests must pass.
|
|
90
|
+
|
|
91
|
+
### Phase 3: Update `invokeProposerStrategy` and `submitProposal`
|
|
92
|
+
|
|
93
|
+
**Step 3a:** In `src/dialai/api.ts`, change the return type of `invokeProposerStrategy` (line 569) from `Promise<{ transitionName: string; toState: string; reasoning: string }>` to `Promise<ProposerStrategyResult>`. Import `ProposerStrategyResult` if not already imported. No other changes to this function are needed — it already delegates to functions that return `ProposerStrategyResult`.
|
|
94
|
+
|
|
95
|
+
**Step 3b:** In `submitProposal`, update the strategy-invocation branch. Before the `if (!finalTransitionName)` block, declare:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
let finalCostUSD = costUSD;
|
|
99
|
+
let finalLatencyMsec = latencyMsec;
|
|
100
|
+
let finalNumInputTokens = numInputTokens;
|
|
101
|
+
let finalNumOutputTokens = numOutputTokens;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Inside the block, after setting `finalReasoning`, add:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
finalCostUSD = finalCostUSD ?? result.costUSD;
|
|
108
|
+
finalLatencyMsec = finalLatencyMsec ?? result.latencyMsec;
|
|
109
|
+
finalNumInputTokens = finalNumInputTokens ?? result.numInputTokens;
|
|
110
|
+
finalNumOutputTokens = finalNumOutputTokens ?? result.numOutputTokens;
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Then use the `final*` variables in the proposal construction object instead of the raw `costUSD`, `latencyMsec`, `numInputTokens`, `numOutputTokens`.
|
|
114
|
+
|
|
115
|
+
**Validate:** Run `npm run typecheck && npm test`. All must pass.
|
|
116
|
+
|
|
117
|
+
### Phase 4: Tests
|
|
118
|
+
|
|
119
|
+
Add these tests:
|
|
120
|
+
|
|
121
|
+
**In `src/dialai/llm.test.ts`:**
|
|
122
|
+
|
|
123
|
+
1. **`executeProposerLlm` returns metrics.** Mock `callLlm` to return `{ content: '{"transitionName":"close","toState":"closed","reasoning":"test"}', usage: { prompt_tokens: 100, completion_tokens: 50 } }`. Assert the result includes `latencyMsec` (a number > 0), `numInputTokens` (100), `numOutputTokens` (50).
|
|
124
|
+
|
|
125
|
+
**In `tests/unit/submit-proposal.test.ts`:**
|
|
126
|
+
|
|
127
|
+
2. **Strategy metrics flow to proposal.** Register a proposer with a `strategyFn` that returns `{ transitionName: "t", toState: "s", reasoning: "r", latencyMsec: 500, numInputTokens: 200, numOutputTokens: 80 }`. Call `submitProposal({ sessionId, specialistId })` without `transitionName`. Assert the stored proposal has `latencyMsec: 500`, `numInputTokens: 200`, `numOutputTokens: 80`.
|
|
128
|
+
|
|
129
|
+
3. **Opts override strategy metrics.** Same setup as test 2, but call `submitProposal({ sessionId, specialistId, latencyMsec: 999 })`. Assert proposal has `latencyMsec: 999` (opts wins), `numInputTokens: 200` (strategy fills gap).
|
|
130
|
+
|
|
131
|
+
4. **Backward compat — no metrics from strategy.** Register a proposer with a `strategyFn` that returns `{ transitionName: "t", toState: "s", reasoning: "r" }` (no metric fields). Call `submitProposal` without `transitionName`. Assert the proposal is created with `latencyMsec: undefined`, `numInputTokens: undefined`. No crash.
|
|
132
|
+
|
|
133
|
+
**Validate:** Run `npm run ci`. All must pass (typecheck + lint + test + build).
|
|
134
|
+
|
|
135
|
+
## Acceptance Criteria
|
|
136
|
+
|
|
137
|
+
### Functional
|
|
138
|
+
|
|
139
|
+
- `ProposerStrategyResult` has optional `costUSD`, `latencyMsec`, `numInputTokens`, `numOutputTokens` fields
|
|
140
|
+
- `executeProposerLlm` returns `latencyMsec` and token counts from the underlying `callLlm` response
|
|
141
|
+
- `invokeProposerStrategy` return type is `ProposerStrategyResult`
|
|
142
|
+
- `submitProposal` populates proposal metrics from strategy result when caller does not provide them via opts
|
|
143
|
+
- Caller-provided opts values always take precedence over strategy-returned values
|
|
144
|
+
|
|
145
|
+
### Quality
|
|
146
|
+
|
|
147
|
+
- Existing `strategyFn` implementations that do not return the new fields continue to work
|
|
148
|
+
- All existing tests pass without modification
|
|
149
|
+
- New tests cover: metrics flow, opts precedence, backward compat
|
|
150
|
+
|
|
151
|
+
### Operational
|
|
152
|
+
|
|
153
|
+
- `npm run ci` passes (typecheck + lint + test + build)
|
|
154
|
+
|
|
155
|
+
## Failure and Recovery Rules
|
|
156
|
+
|
|
157
|
+
1. Run `npm run typecheck` after Phase 1. Run `npm test` after Phases 2 and 3. Run `npm run ci` after Phase 4.
|
|
158
|
+
2. If adding fields to `ProposerStrategyResult` causes type errors, verify they are marked optional. If a destructure or spread assumes the exact shape, update it.
|
|
159
|
+
3. If `executeProposerLlm` tests fail because `callLlm` is hard to mock, follow the pattern in `src/dialai/llm-audit.test.ts` (mock `globalThis.fetch`, set `OPENROUTER_API_TOKEN`).
|
|
160
|
+
4. If changing `invokeProposerStrategy` return type causes downstream type errors, check that `submitProposal` correctly accesses the new optional fields with nullish coalescing.
|
|
161
|
+
5. Do not declare completion while any acceptance criterion is unmet.
|
|
162
|
+
|
|
163
|
+
## Completion Signal
|
|
164
|
+
|
|
165
|
+
Output exactly `COMPLETE` only when:
|
|
166
|
+
- All acceptance criteria are met
|
|
167
|
+
- `npm run ci` passes
|
|
168
|
+
- No blocking errors remain
|
|
169
|
+
|
|
170
|
+
## Ralph Prompt Draft
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
Implement operational metrics on ProposerStrategyResult.
|
|
174
|
+
|
|
175
|
+
Spec location: .claude/specs/proposal-metadata-update.md
|
|
176
|
+
|
|
177
|
+
Constraints:
|
|
178
|
+
- Do not change callLlm's return type or signature
|
|
179
|
+
- Do not change ArbiterStrategyResult
|
|
180
|
+
- Do not modify executeContextWebhookProposer (metrics cascade from executeProposerLlm)
|
|
181
|
+
- All new fields on ProposerStrategyResult must be optional
|
|
182
|
+
|
|
183
|
+
Files to modify:
|
|
184
|
+
- src/dialai/types.ts (ProposerStrategyResult)
|
|
185
|
+
- src/dialai/llm.ts (executeProposerLlm)
|
|
186
|
+
- src/dialai/api.ts (invokeProposerStrategy return type, submitProposal merge logic)
|
|
187
|
+
- tests/unit/submit-proposal.test.ts (3 new tests)
|
|
188
|
+
- src/dialai/llm.test.ts (1 new test)
|
|
189
|
+
|
|
190
|
+
Required deliverables:
|
|
191
|
+
- ProposerStrategyResult has optional costUSD, latencyMsec, numInputTokens, numOutputTokens
|
|
192
|
+
- executeProposerLlm returns latencyMsec and token counts from callLlm
|
|
193
|
+
- invokeProposerStrategy return type updated to ProposerStrategyResult
|
|
194
|
+
- submitProposal merges strategy metrics into proposal (opts take precedence)
|
|
195
|
+
- 4 new tests covering metrics flow, opts precedence, and backward compat
|
|
196
|
+
|
|
197
|
+
Acceptance criteria:
|
|
198
|
+
- npm run ci passes (typecheck + lint + test + build)
|
|
199
|
+
- All existing tests pass without modification
|
|
200
|
+
- New tests verify: (1) executeProposerLlm returns metrics, (2) strategy metrics flow to proposal, (3) opts override strategy metrics, (4) missing metrics don't crash
|
|
201
|
+
|
|
202
|
+
Execution rules:
|
|
203
|
+
1. Work in phase order: types → llm.ts → api.ts → tests
|
|
204
|
+
2. Run npm run typecheck after Phase 1
|
|
205
|
+
3. Run npm test after Phases 2 and 3
|
|
206
|
+
4. Run npm run ci after Phase 4
|
|
207
|
+
5. If tests fail, inspect and fix the root cause before continuing
|
|
208
|
+
6. If blocked after 3 attempts on the same issue, report the blocker
|
|
209
|
+
|
|
210
|
+
Output exactly COMPLETE when all criteria are met.
|
|
211
|
+
```
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# submitProposal: Remove `final*` Prefix Pattern
|
|
2
|
+
|
|
3
|
+
## Executive Summary
|
|
4
|
+
|
|
5
|
+
Refactor `submitProposal` in `api.ts` to drop the `final*` variable prefix introduced in commit `0f273a9`. Instead of destructuring all opts fields as `const` and then creating parallel `let final*` variables, destructure the immutable fields as `const` and declare the mutable ones as `let` directly. Also bring `metaJson` into the same pattern so strategy-returned `metaJson` flows to the proposal.
|
|
6
|
+
|
|
7
|
+
## Objective
|
|
8
|
+
|
|
9
|
+
Simplify `submitProposal` so the "opts wins, strategy fills gaps" merging logic reads naturally without the `final*` indirection, and add `metaJson` to the merge pattern so it works identically to the other fields.
|
|
10
|
+
|
|
11
|
+
## In Scope
|
|
12
|
+
|
|
13
|
+
- Refactor variable declarations in `submitProposal`
|
|
14
|
+
- Bring `metaJson` into the same `let` / `??=` pattern as `costUSD`, `latencyMsec`, etc.
|
|
15
|
+
- Update existing tests if assertions reference the old behavior
|
|
16
|
+
- Add `metaJson` to `ProposerStrategyResult` (if not already there from the enriched-transitions spec)
|
|
17
|
+
|
|
18
|
+
## Out of Scope
|
|
19
|
+
|
|
20
|
+
- Changes to `submitArbitration` (it doesn't have the `final*` pattern)
|
|
21
|
+
- Changes to any other function
|
|
22
|
+
|
|
23
|
+
## Assumptions and Constraints
|
|
24
|
+
|
|
25
|
+
- `ProposerStrategyResult` must have an optional `metaJson` field. If the enriched-transitions spec runs first, it will already exist. If this spec runs first, add it.
|
|
26
|
+
- The merge semantics are: caller-provided opts value wins; strategy-returned value fills the gap.
|
|
27
|
+
- `transitionName`, `toState`, and `reasoning` also follow the same pattern — they just don't use the `final*` prefix inconsistently (they already do use it). Clean those up too.
|
|
28
|
+
|
|
29
|
+
## Files to Modify
|
|
30
|
+
|
|
31
|
+
| File | Action |
|
|
32
|
+
|------|--------|
|
|
33
|
+
| `src/dialai/api.ts` | Refactor `submitProposal` variable declarations |
|
|
34
|
+
| `src/dialai/types.ts` | Add `metaJson` to `ProposerStrategyResult` if not already present |
|
|
35
|
+
| `tests/unit/submit-proposal.test.ts` | Add tests for metaJson merging |
|
|
36
|
+
| `website/docs/api/types.md` | Add `metaJson` field to `ProposerStrategyResult` docs |
|
|
37
|
+
| `.claude/skills/dial-machine/references/api-reference.md` | Add `metaJson` field to `ProposerStrategyResult` docs |
|
|
38
|
+
|
|
39
|
+
## Implementation Plan
|
|
40
|
+
|
|
41
|
+
### Phase 1: Refactor `submitProposal`
|
|
42
|
+
|
|
43
|
+
**Before (current code, lines 639-717):**
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
export async function submitProposal(
|
|
47
|
+
opts: SubmitProposalOptions
|
|
48
|
+
): Promise<Proposal> {
|
|
49
|
+
const {
|
|
50
|
+
sessionId,
|
|
51
|
+
specialistId,
|
|
52
|
+
roundId,
|
|
53
|
+
transitionName,
|
|
54
|
+
reasoning,
|
|
55
|
+
metaJson,
|
|
56
|
+
costUSD,
|
|
57
|
+
latencyMsec,
|
|
58
|
+
numInputTokens,
|
|
59
|
+
numOutputTokens,
|
|
60
|
+
} = opts;
|
|
61
|
+
// ...
|
|
62
|
+
let finalTransitionName = transitionName;
|
|
63
|
+
let finalToState: string | undefined;
|
|
64
|
+
let finalReasoning = reasoning;
|
|
65
|
+
let finalCostUSD = costUSD;
|
|
66
|
+
let finalLatencyMsec = latencyMsec;
|
|
67
|
+
let finalNumInputTokens = numInputTokens;
|
|
68
|
+
let finalNumOutputTokens = numOutputTokens;
|
|
69
|
+
|
|
70
|
+
if (!finalTransitionName) {
|
|
71
|
+
// ...
|
|
72
|
+
finalTransitionName = result.transitionName;
|
|
73
|
+
finalToState = result.toState;
|
|
74
|
+
finalReasoning = finalReasoning ?? result.reasoning;
|
|
75
|
+
finalCostUSD = finalCostUSD ?? result.costUSD;
|
|
76
|
+
// ...
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const proposal: Proposal = {
|
|
80
|
+
// ...
|
|
81
|
+
transitionName: finalTransitionName,
|
|
82
|
+
toState: finalToState,
|
|
83
|
+
reasoning: finalReasoning ?? "",
|
|
84
|
+
metaJson, // <-- BUG: ignores strategy metaJson
|
|
85
|
+
costUSD: finalCostUSD,
|
|
86
|
+
// ...
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**After:**
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
export async function submitProposal(
|
|
95
|
+
opts: SubmitProposalOptions
|
|
96
|
+
): Promise<Proposal> {
|
|
97
|
+
const { sessionId, specialistId, roundId } = opts;
|
|
98
|
+
|
|
99
|
+
let transitionName = opts.transitionName;
|
|
100
|
+
let toState: string | undefined;
|
|
101
|
+
let reasoning = opts.reasoning;
|
|
102
|
+
let metaJson = opts.metaJson;
|
|
103
|
+
let costUSD = opts.costUSD;
|
|
104
|
+
let latencyMsec = opts.latencyMsec;
|
|
105
|
+
let numInputTokens = opts.numInputTokens;
|
|
106
|
+
let numOutputTokens = opts.numOutputTokens;
|
|
107
|
+
|
|
108
|
+
const session = await getSession(sessionId);
|
|
109
|
+
const specialist = await getStore().getSpecialist(specialistId);
|
|
110
|
+
|
|
111
|
+
if (!specialist) {
|
|
112
|
+
throw new Error(`Specialist not found: ${specialistId}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (specialist.role !== "proposer") {
|
|
116
|
+
throw new Error(`Specialist ${specialistId} is not a proposer`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const proposer = specialist;
|
|
120
|
+
const effectiveRoundId = roundId ?? session.currentRoundId;
|
|
121
|
+
const isHuman = proposer.isHuman ?? false;
|
|
122
|
+
|
|
123
|
+
if (!transitionName) {
|
|
124
|
+
const ctx = buildProposerContext(session);
|
|
125
|
+
const result = await invokeProposerStrategy(proposer, ctx);
|
|
126
|
+
transitionName = result.transitionName;
|
|
127
|
+
toState = result.toState;
|
|
128
|
+
reasoning = reasoning ?? result.reasoning;
|
|
129
|
+
metaJson = metaJson ?? result.metaJson;
|
|
130
|
+
costUSD = costUSD ?? result.costUSD;
|
|
131
|
+
latencyMsec = latencyMsec ?? result.latencyMsec;
|
|
132
|
+
numInputTokens = numInputTokens ?? result.numInputTokens;
|
|
133
|
+
numOutputTokens = numOutputTokens ?? result.numOutputTokens;
|
|
134
|
+
} else {
|
|
135
|
+
const currentStateDef = session.machine.states[session.currentState];
|
|
136
|
+
if (!currentStateDef?.transitions?.[transitionName]) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Invalid transition "${transitionName}" from state "${session.currentState}"`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
toState = currentStateDef.transitions[transitionName];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const proposal: Proposal = {
|
|
145
|
+
proposalId: generateUUID(),
|
|
146
|
+
sessionId,
|
|
147
|
+
roundId: effectiveRoundId,
|
|
148
|
+
specialistId,
|
|
149
|
+
isHuman,
|
|
150
|
+
transitionName,
|
|
151
|
+
toState,
|
|
152
|
+
reasoning: reasoning ?? "",
|
|
153
|
+
metaJson,
|
|
154
|
+
costUSD,
|
|
155
|
+
latencyMsec,
|
|
156
|
+
numInputTokens,
|
|
157
|
+
numOutputTokens,
|
|
158
|
+
createdAt: new Date(),
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
await getStore().setProposal(proposal);
|
|
162
|
+
return proposal;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Key changes:
|
|
167
|
+
1. `const` destructuring only for truly immutable fields: `sessionId`, `specialistId`, `roundId`
|
|
168
|
+
2. All mutable fields declared as `let` from `opts.*`
|
|
169
|
+
3. No `final*` prefix anywhere
|
|
170
|
+
4. `metaJson` follows the same `??=` pattern as the metrics fields
|
|
171
|
+
5. `toState` replaces `finalToState` (was already the only one using a different name)
|
|
172
|
+
|
|
173
|
+
### Phase 2: Add `metaJson` to `ProposerStrategyResult`
|
|
174
|
+
|
|
175
|
+
In `types.ts`, add to `ProposerStrategyResult` if not already present:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
/** Structured metadata from the strategy (e.g., tool arguments) */
|
|
179
|
+
metaJson?: Record<string, unknown>;
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Validate:** `npm run typecheck`
|
|
183
|
+
|
|
184
|
+
### Phase 3: Tests
|
|
185
|
+
|
|
186
|
+
**In `tests/unit/submit-proposal.test.ts`:**
|
|
187
|
+
|
|
188
|
+
**Test 1: strategy-returned metaJson flows to proposal**
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
Setup: strategyFn returns { transitionName: "approve", toState: "approved", reasoning: "r", metaJson: { key: "from-strategy" } }
|
|
192
|
+
Call: submitProposal({ sessionId, specialistId }) — no metaJson in opts
|
|
193
|
+
Assert: proposal.metaJson deep-equals { key: "from-strategy" }
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Test 2: caller-provided metaJson takes precedence over strategy metaJson**
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
Setup: same strategyFn returning metaJson: { key: "from-strategy" }
|
|
200
|
+
Call: submitProposal({ sessionId, specialistId, metaJson: { key: "from-caller" } })
|
|
201
|
+
Assert: proposal.metaJson deep-equals { key: "from-caller" }
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Test 3: no metaJson from either source**
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
Setup: strategyFn returns { transitionName: "approve", toState: "approved", reasoning: "r" } (no metaJson)
|
|
208
|
+
Call: submitProposal({ sessionId, specialistId })
|
|
209
|
+
Assert: proposal.metaJson is undefined
|
|
210
|
+
Assert: no crash
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Existing tests (`strategy metrics flow to proposal`, `opts override strategy metrics`, `backward compat — no metrics from strategy`) must continue to pass without modification.
|
|
214
|
+
|
|
215
|
+
**Validate:** `npm run ci`
|
|
216
|
+
|
|
217
|
+
### Phase 4: Update Documentation
|
|
218
|
+
|
|
219
|
+
**`website/docs/api/types.md` — `ProposerStrategyResult` section:**
|
|
220
|
+
|
|
221
|
+
Add `metaJson` to the interface listing. Currently shows:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
interface ProposerStrategyResult {
|
|
225
|
+
transitionName: string;
|
|
226
|
+
toState: string;
|
|
227
|
+
reasoning: string;
|
|
228
|
+
costUSD?: number;
|
|
229
|
+
latencyMsec?: number;
|
|
230
|
+
numInputTokens?: number;
|
|
231
|
+
numOutputTokens?: number;
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Add after `reasoning`:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
metaJson?: Record<string, unknown>; // Structured metadata (e.g., tool arguments)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Update the prose above the code block to mention metaJson: "The optional metric and metadata fields are merged into the resulting `Proposal` when the strategy is invoked via `submitProposal`. Values passed via `SubmitProposalOptions` take precedence over strategy-returned values."
|
|
242
|
+
|
|
243
|
+
**`.claude/skills/dial-machine/references/api-reference.md` — same change:**
|
|
244
|
+
|
|
245
|
+
Add `metaJson` to `ProposerStrategyResult` in the same position.
|
|
246
|
+
|
|
247
|
+
## Acceptance Criteria
|
|
248
|
+
|
|
249
|
+
### Functional
|
|
250
|
+
|
|
251
|
+
- `submitProposal` has no variables with the `final` prefix
|
|
252
|
+
- Immutable fields (`sessionId`, `specialistId`, `roundId`) are `const` destructured
|
|
253
|
+
- Mutable fields (`transitionName`, `reasoning`, `metaJson`, `costUSD`, `latencyMsec`, `numInputTokens`, `numOutputTokens`, `toState`) are `let` declarations
|
|
254
|
+
- `metaJson` merging follows the same `opts ?? strategy` pattern as the metrics fields
|
|
255
|
+
- Strategy-returned `metaJson` flows to the proposal when caller doesn't provide it
|
|
256
|
+
- Caller-provided `metaJson` takes precedence over strategy-returned `metaJson`
|
|
257
|
+
|
|
258
|
+
### Quality
|
|
259
|
+
|
|
260
|
+
- All existing `submit-proposal.test.ts` tests pass unchanged
|
|
261
|
+
- All existing tests across the project pass
|
|
262
|
+
- `npm run typecheck` passes
|
|
263
|
+
- `npm run lint` passes
|
|
264
|
+
|
|
265
|
+
### Operational
|
|
266
|
+
|
|
267
|
+
- `npm run ci` passes
|
|
268
|
+
|
|
269
|
+
## Failure and Recovery Rules
|
|
270
|
+
|
|
271
|
+
1. If `npm run lint` complains about variable shadowing (the `let` declarations shadow the destructured names), that means the `const` destructuring still includes those fields. Remove them from the destructuring.
|
|
272
|
+
2. If existing tests break, check that the proposal construction object uses the same field names (no `final*` prefix).
|
|
273
|
+
3. The `toState` line in the `else` branch (`toState = currentStateDef.transitions[transitionName]`) will need updating if the enriched-transitions spec has already run (it would return a `TransitionDefinition` instead of a string). If so, use `typeof` check.
|
|
274
|
+
|
|
275
|
+
## Completion Signal
|
|
276
|
+
|
|
277
|
+
Output exactly `COMPLETE` only when:
|
|
278
|
+
- All acceptance criteria are met
|
|
279
|
+
- `npm run ci` passes
|
|
280
|
+
- No `final*` variables remain in `submitProposal`
|
|
281
|
+
|
|
282
|
+
## Ralph Prompt Draft
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
Refactor submitProposal in src/dialai/api.ts to remove the final* variable prefix.
|
|
286
|
+
|
|
287
|
+
Spec location: .claude/specs/submit-proposal-cleanup.md
|
|
288
|
+
|
|
289
|
+
The current code destructures all opts fields as const, then creates parallel
|
|
290
|
+
let final* variables for the mutable ones. This is noisy and metaJson was
|
|
291
|
+
missed (line 708 uses raw opts.metaJson, so strategy-returned metaJson never
|
|
292
|
+
reaches the proposal).
|
|
293
|
+
|
|
294
|
+
Required changes:
|
|
295
|
+
- const destructure only: sessionId, specialistId, roundId
|
|
296
|
+
- let declarations for: transitionName, toState, reasoning, metaJson, costUSD,
|
|
297
|
+
latencyMsec, numInputTokens, numOutputTokens (initialized from opts.*)
|
|
298
|
+
- Strategy merge block: field = field ?? result.field (no final prefix)
|
|
299
|
+
- metaJson follows the same pattern as the metrics fields
|
|
300
|
+
- Proposal construction uses the let variables directly (no final prefix)
|
|
301
|
+
- Add metaJson to ProposerStrategyResult in types.ts if not already present
|
|
302
|
+
|
|
303
|
+
Tests to add in tests/unit/submit-proposal.test.ts:
|
|
304
|
+
1. Strategy-returned metaJson flows to proposal when caller omits it
|
|
305
|
+
2. Caller-provided metaJson wins over strategy-returned metaJson
|
|
306
|
+
3. No metaJson from either source — proposal.metaJson is undefined
|
|
307
|
+
|
|
308
|
+
Docs to update:
|
|
309
|
+
- website/docs/api/types.md — add metaJson to ProposerStrategyResult
|
|
310
|
+
- .claude/skills/dial-machine/references/api-reference.md — same
|
|
311
|
+
|
|
312
|
+
Acceptance criteria:
|
|
313
|
+
- No final* variables in submitProposal
|
|
314
|
+
- metaJson merges with same opts-wins semantics as costUSD etc.
|
|
315
|
+
- All existing tests pass unchanged
|
|
316
|
+
- npm run ci passes
|
|
317
|
+
|
|
318
|
+
Output exactly COMPLETE when all criteria are met.
|
|
319
|
+
```
|
package/dist/dialai/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/dialai/api.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/dialai/api.ts"],"names":[],"mappings":"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"}
|
package/dist/dialai/api.js
CHANGED
|
@@ -480,7 +480,15 @@ async function invokeArbiterStrategy(arbiter, ctx) {
|
|
|
480
480
|
* If transitionName is omitted, invokes the specialist's registered strategy.
|
|
481
481
|
*/
|
|
482
482
|
export async function submitProposal(opts) {
|
|
483
|
-
const { sessionId, specialistId, roundId
|
|
483
|
+
const { sessionId, specialistId, roundId } = opts;
|
|
484
|
+
let transitionName = opts.transitionName;
|
|
485
|
+
let toState;
|
|
486
|
+
let reasoning = opts.reasoning;
|
|
487
|
+
let metaJson = opts.metaJson;
|
|
488
|
+
let costUSD = opts.costUSD;
|
|
489
|
+
let latencyMsec = opts.latencyMsec;
|
|
490
|
+
let numInputTokens = opts.numInputTokens;
|
|
491
|
+
let numOutputTokens = opts.numOutputTokens;
|
|
484
492
|
const session = await getSession(sessionId);
|
|
485
493
|
const specialist = await getStore().getSpecialist(specialistId);
|
|
486
494
|
if (!specialist) {
|
|
@@ -492,32 +500,26 @@ export async function submitProposal(opts) {
|
|
|
492
500
|
const proposer = specialist;
|
|
493
501
|
const effectiveRoundId = roundId ?? session.currentRoundId;
|
|
494
502
|
const isHuman = proposer.isHuman ?? false;
|
|
495
|
-
let finalTransitionName = transitionName;
|
|
496
|
-
let finalToState;
|
|
497
|
-
let finalReasoning = reasoning;
|
|
498
|
-
let finalCostUSD = costUSD;
|
|
499
|
-
let finalLatencyMsec = latencyMsec;
|
|
500
|
-
let finalNumInputTokens = numInputTokens;
|
|
501
|
-
let finalNumOutputTokens = numOutputTokens;
|
|
502
503
|
// If transitionName not provided, invoke strategy
|
|
503
|
-
if (!
|
|
504
|
+
if (!transitionName) {
|
|
504
505
|
const ctx = buildProposerContext(session);
|
|
505
506
|
const result = await invokeProposerStrategy(proposer, ctx);
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
507
|
+
transitionName = result.transitionName;
|
|
508
|
+
toState = result.toState;
|
|
509
|
+
reasoning = reasoning ?? result.reasoning;
|
|
510
|
+
metaJson = metaJson ?? result.metaJson;
|
|
511
|
+
costUSD = costUSD ?? result.costUSD;
|
|
512
|
+
latencyMsec = latencyMsec ?? result.latencyMsec;
|
|
513
|
+
numInputTokens = numInputTokens ?? result.numInputTokens;
|
|
514
|
+
numOutputTokens = numOutputTokens ?? result.numOutputTokens;
|
|
513
515
|
}
|
|
514
516
|
else {
|
|
515
517
|
// Validate the transition
|
|
516
518
|
const currentStateDef = session.machine.states[session.currentState];
|
|
517
|
-
if (!currentStateDef?.transitions?.[
|
|
518
|
-
throw new Error(`Invalid transition "${
|
|
519
|
+
if (!currentStateDef?.transitions?.[transitionName]) {
|
|
520
|
+
throw new Error(`Invalid transition "${transitionName}" from state "${session.currentState}"`);
|
|
519
521
|
}
|
|
520
|
-
|
|
522
|
+
toState = currentStateDef.transitions[transitionName];
|
|
521
523
|
}
|
|
522
524
|
const proposal = {
|
|
523
525
|
proposalId: generateUUID(),
|
|
@@ -525,14 +527,14 @@ export async function submitProposal(opts) {
|
|
|
525
527
|
roundId: effectiveRoundId,
|
|
526
528
|
specialistId,
|
|
527
529
|
isHuman,
|
|
528
|
-
transitionName
|
|
529
|
-
toState
|
|
530
|
-
reasoning:
|
|
530
|
+
transitionName,
|
|
531
|
+
toState,
|
|
532
|
+
reasoning: reasoning ?? "",
|
|
531
533
|
metaJson,
|
|
532
|
-
costUSD
|
|
533
|
-
latencyMsec
|
|
534
|
-
numInputTokens
|
|
535
|
-
numOutputTokens
|
|
534
|
+
costUSD,
|
|
535
|
+
latencyMsec,
|
|
536
|
+
numInputTokens,
|
|
537
|
+
numOutputTokens,
|
|
536
538
|
createdAt: new Date(),
|
|
537
539
|
};
|
|
538
540
|
await getStore().setProposal(proposal);
|