ns-kiro-core 0.1.2 → 0.2.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.
@@ -0,0 +1,49 @@
1
+ import type { KiroAdditionalModelRequestFields } from "./effort.js";
2
+ import type { KiroModel } from "./models.js";
3
+ import { type KiroHistoryEntry, type KiroUserInputMessage } from "./transform.js";
4
+ import type { KiroMessage, KiroTool } from "./types.js";
5
+ export interface KiroRequest {
6
+ conversationState: {
7
+ chatTriggerType: "MANUAL";
8
+ agentTaskType: "vibe";
9
+ conversationId: string;
10
+ currentMessage: {
11
+ userInputMessage: KiroUserInputMessage;
12
+ };
13
+ history?: KiroHistoryEntry[];
14
+ };
15
+ additionalModelRequestFields?: KiroAdditionalModelRequestFields;
16
+ profileArn: string;
17
+ agentMode?: string;
18
+ }
19
+ export interface BuildKiroRequestParams {
20
+ messages: KiroMessage[];
21
+ model: KiroModel;
22
+ kiroModelId: string;
23
+ /** Already carries any thinking markers the caller decided to prepend. */
24
+ systemPrompt: string;
25
+ tools?: KiroTool[];
26
+ conversationId: string;
27
+ profileArn: string;
28
+ additionalModelRequestFields?: KiroAdditionalModelRequestFields;
29
+ }
30
+ /**
31
+ * The request plus what was actually put on the wire.
32
+ *
33
+ * The counts describe the post-repair body, not the caller's input: they exist
34
+ * to be correlated against a rejection, so they must describe the bytes sent.
35
+ */
36
+ export interface BuiltKiroRequest {
37
+ request: KiroRequest;
38
+ wireHistoryLength: number;
39
+ wireContentLength: number;
40
+ hasImages: boolean;
41
+ toolResultCount: number;
42
+ }
43
+ /**
44
+ * Assemble the body for one Kiro runtime call.
45
+ *
46
+ * Throws when the local history exceeds its budget, rather than silently
47
+ * dropping context the caller believes it sent.
48
+ */
49
+ export declare function buildKiroRequest(params: BuildKiroRequestParams): BuiltKiroRequest;
@@ -0,0 +1,229 @@
1
+ // ABOUTME: Builds the Kiro runtime request body from a neutral conversation.
2
+ // ABOUTME: Pure and side-effect free, so request shaping is testable without a transport.
3
+ import { debugLog } from "./debug.js";
4
+ import { addPlaceholderTools, assertHistoryWithinLimit, HISTORY_LIMIT, HISTORY_LIMIT_CONTEXT_WINDOW, prepareHistory, } from "./history.js";
5
+ import { isKiroToolStructureRule, kiroConversationEntries, repairKiroConversation } from "./history-validator.js";
6
+ import { buildHistory, convertImagesToKiro, convertToolsToKiro, EMPTY_CONTENT_PLACEHOLDER, extractImages, getContentText, normalizeMessages, relocateDisplacedToolResults, sanitizeSurrogates, TOOL_RESULT_LIMIT, toKiroToolUseId, truncate, } from "./transform.js";
7
+ import { TRUNCATION_NOTICE, wasPreviousResponseTruncated } from "./truncation.js";
8
+ function toolResultOf(message) {
9
+ return {
10
+ content: [{ text: truncate(getContentText(message), TOOL_RESULT_LIMIT) }],
11
+ status: message.isError ? "error" : "success",
12
+ toolUseId: toKiroToolUseId(message.toolCallId),
13
+ };
14
+ }
15
+ /**
16
+ * Assemble the body for one Kiro runtime call.
17
+ *
18
+ * Throws when the local history exceeds its budget, rather than silently
19
+ * dropping context the caller believes it sent.
20
+ */
21
+ export function buildKiroRequest(params) {
22
+ const { messages, model, kiroModelId, systemPrompt, tools, conversationId, profileArn } = params;
23
+ // Relocate a tool result that arrived behind a later assistant turn than the
24
+ // one that called it, before anything positional runs. Interleaved concurrent
25
+ // tool executions produce that shape, and `sanitizeHistory` pairs
26
+ // POSITIONALLY, so without this pass the displaced result's issuing assistant
27
+ // is dropped and the real tool output is discarded.
28
+ const normalized = relocateDisplacedToolResults(normalizeMessages(messages));
29
+ const { history: rawHistory, systemPrepended, currentMsgStartIdx, } = buildHistory(normalized, kiroModelId, systemPrompt);
30
+ // Preserve semantic context locally; the host owns lossy compaction.
31
+ const history = prepareHistory(rawHistory, model.input.includes("image"));
32
+ const dynamicHistoryLimit = Math.floor((model.contextWindow / HISTORY_LIMIT_CONTEXT_WINDOW) * HISTORY_LIMIT);
33
+ const currentMessages = normalized.slice(currentMsgStartIdx);
34
+ const firstMsg = currentMessages[0];
35
+ let currentContent = "";
36
+ const currentToolResults = [];
37
+ let currentImages;
38
+ if (firstMsg?.role === "assistant") {
39
+ let armContent = "";
40
+ const armToolUses = [];
41
+ for (const block of firstMsg.content) {
42
+ if (block.type === "text")
43
+ armContent += block.text;
44
+ // Reasoning is deliberately NOT serialized into the assistant text
45
+ // channel, matching `buildHistory`. Flattening it to
46
+ // `<thinking>...</thinking>` writes literal markup into the string the
47
+ // model reads back as its own prior speech.
48
+ else if (block.type === "toolCall") {
49
+ armToolUses.push({ name: block.name, toolUseId: toKiroToolUseId(block.id), input: block.arguments });
50
+ }
51
+ }
52
+ if (armContent || armToolUses.length > 0) {
53
+ const prevArm = history[history.length - 1]?.assistantResponseMessage;
54
+ if (history.length > 0 && !history[history.length - 1]?.userInputMessage && prevArm) {
55
+ // Merge into previous assistant message to maintain alternation
56
+ // without synthetic padding. Join only non-empty sides: a turn that
57
+ // carried only reasoning or only a tool call leaves `armContent`
58
+ // empty, and an unconditional separator would append a bare `\n\n`
59
+ // onto text the model actually produced.
60
+ prevArm.content =
61
+ prevArm.content && armContent ? `${prevArm.content}\n\n${armContent}` : prevArm.content || armContent;
62
+ if (armToolUses.length > 0)
63
+ prevArm.toolUses = [...(prevArm.toolUses || []), ...armToolUses];
64
+ }
65
+ else {
66
+ history.push({
67
+ assistantResponseMessage: {
68
+ content: armContent,
69
+ ...(armToolUses.length > 0 ? { toolUses: armToolUses } : {}),
70
+ },
71
+ });
72
+ }
73
+ }
74
+ const toolResultImages = [];
75
+ for (let i = 1; i < currentMessages.length; i++) {
76
+ const m = currentMessages[i];
77
+ if (m?.role !== "toolResult")
78
+ continue;
79
+ currentToolResults.push(toolResultOf(m));
80
+ toolResultImages.push(...extractImages(m));
81
+ }
82
+ if (toolResultImages.length > 0)
83
+ currentImages = convertImagesToKiro(toolResultImages);
84
+ // A tool turn carries its payload in `userInputMessageContext.toolResults`,
85
+ // so it needs no text. Leaving this empty also leaves the fallback below
86
+ // free to fill in only genuinely payload-less turns.
87
+ currentContent = "";
88
+ }
89
+ else if (firstMsg?.role === "toolResult") {
90
+ const toolResultImages = [];
91
+ for (const m of currentMessages) {
92
+ if (m.role !== "toolResult")
93
+ continue;
94
+ currentToolResults.push(toolResultOf(m));
95
+ toolResultImages.push(...extractImages(m));
96
+ }
97
+ if (toolResultImages.length > 0)
98
+ currentImages = convertImagesToKiro(toolResultImages);
99
+ // Empty by design — `toolResults` is this turn's payload.
100
+ currentContent = "";
101
+ }
102
+ else if (firstMsg?.role === "user") {
103
+ currentContent = getContentText(firstMsg);
104
+ if (systemPrompt && !systemPrepended)
105
+ currentContent = `${systemPrompt}\n\n${currentContent}`;
106
+ }
107
+ // Current assistant tool calls are outbound history too, so enforce the
108
+ // budget only after they have been appended.
109
+ assertHistoryWithinLimit(history, dynamicHistoryLimit);
110
+ if (wasPreviousResponseTruncated(messages)) {
111
+ currentContent = currentContent === "" ? TRUNCATION_NOTICE : `${TRUNCATION_NOTICE}\n\n${currentContent}`;
112
+ }
113
+ // Always synthesize placeholder specs for tool names referenced in history,
114
+ // even when the caller declares none. Without this, a call that inherits a
115
+ // tool-rich conversation but declares no current tools is rejected by Kiro
116
+ // as "Improperly formed request", because history references toolUses with
117
+ // no tool catalog.
118
+ let uimc;
119
+ const baseTools = tools?.length ? convertToolsToKiro(tools) : [];
120
+ const finalTools = history.length > 0 ? addPlaceholderTools(baseTools, history) : baseTools;
121
+ if (currentToolResults.length > 0 || finalTools.length > 0) {
122
+ uimc = {};
123
+ if (currentToolResults.length > 0)
124
+ uimc.toolResults = currentToolResults;
125
+ if (finalTools.length > 0)
126
+ uimc.tools = finalTools;
127
+ }
128
+ if (firstMsg?.role === "user") {
129
+ const imgs = extractImages(firstMsg);
130
+ if (imgs.length > 0)
131
+ currentImages = convertImagesToKiro(imgs);
132
+ }
133
+ // A turn with neither text nor tool results has no payload at all: an
134
+ // image-only user message, or an empty-text one. Send a neutral prompt so
135
+ // its attachments still reach the model.
136
+ //
137
+ // The `currentToolResults` guard is load-bearing. Without it this line
138
+ // refills every tool turn that deliberately left `currentContent` empty.
139
+ // Kiro's rule is content **or** tool results — see EMPTY_CONTENT_PLACEHOLDER.
140
+ if (currentContent === "" && currentToolResults.length === 0)
141
+ currentContent = EMPTY_CONTENT_PLACEHOLDER;
142
+ // Pre-send REPAIR against the rules first-party Kiro Agent enforces.
143
+ // `prepareHistory` covers the shapes this provider itself produces, but not
144
+ // every shape a caller can hand us: `sanitizeHistory` tests tool pairing by
145
+ // POSITION, so an assistant entry with `toolUses` survives whenever the next
146
+ // entry carries any `toolResults` at all, matching ids or not, and
147
+ // `injectSyntheticToolCalls` only rescues orphaned RESULTS. A mismatched
148
+ // pair — both partners present, paired with each other's counterpart —
149
+ // passes both passes untouched and is rejected on the wire with
150
+ // `400 TOOL_USE_RESULT_MISMATCH`.
151
+ //
152
+ // Repair runs on the WHOLE conversation and is split back afterwards.
153
+ // Repairing `history` alone would be wrong in the ordinary case: its last
154
+ // entry is normally the assistant whose `toolUses` this very request
155
+ // answers, so rule 4 would synthesize a FAILED result for a call whose real
156
+ // output is sitting in the current message.
157
+ const conversationEntries = kiroConversationEntries(history, {
158
+ content: currentContent,
159
+ modelId: kiroModelId,
160
+ origin: "KIRO_CLI",
161
+ ...(uimc ? { userInputMessageContext: uimc } : {}),
162
+ });
163
+ const repair = repairKiroConversation(conversationEntries);
164
+ if (repair.diagnostics.length > 0) {
165
+ debugLog("request.invariants", { errors: repair.diagnostics, remaining: repair.remaining });
166
+ }
167
+ // Split back. Repair keeps the current message last in every case but total
168
+ // collapse, where a conversation that is *only* a bare tool-result carrier
169
+ // has no valid opening entry and step 1 consumes it.
170
+ const repairedCurrent = repair.entries[repair.entries.length - 1]?.userInputMessage;
171
+ // Read the repaired context EXACTLY, including when repair removed it. A
172
+ // `?? uimc` fallback would undo the repair in the one case that matters
173
+ // most: stripping every orphaned tool result leaves a turn with no context
174
+ // at all, and falling back would put the orphans — the shape the backend
175
+ // rejects — straight back onto the wire.
176
+ let wireHistory;
177
+ let wireContent;
178
+ let wireUimc;
179
+ if (repairedCurrent) {
180
+ wireHistory = repair.entries.slice(0, -1);
181
+ wireContent = repairedCurrent.content;
182
+ wireUimc = repairedCurrent.userInputMessageContext;
183
+ }
184
+ else {
185
+ // Collapsed. Apply what repair would have applied to a lone carrier: drop
186
+ // the results that answer nothing, keep any tool catalog, and give the
187
+ // empty turn the neutral prompt.
188
+ wireHistory = [];
189
+ wireContent = currentContent || EMPTY_CONTENT_PLACEHOLDER;
190
+ wireUimc = uimc?.tools?.length ? { tools: uimc.tools } : undefined;
191
+ }
192
+ if (repair.remaining.length > 0) {
193
+ const structural = repair.remaining.filter((e) => isKiroToolStructureRule(e.rule));
194
+ if (structural.length > 0) {
195
+ console.warn(`[kiro-core] outbound history still violates ${structural
196
+ .map((e) => `${e.rule}@${e.index}`)
197
+ .join(", ")} after repair — Kiro may reject this request`);
198
+ }
199
+ }
200
+ return {
201
+ request: {
202
+ conversationState: {
203
+ chatTriggerType: "MANUAL",
204
+ agentTaskType: "vibe",
205
+ conversationId,
206
+ currentMessage: {
207
+ userInputMessage: {
208
+ content: sanitizeSurrogates(wireContent),
209
+ modelId: kiroModelId,
210
+ origin: "KIRO_CLI",
211
+ ...(currentImages ? { images: currentImages } : {}),
212
+ ...(wireUimc ? { userInputMessageContext: wireUimc } : {}),
213
+ },
214
+ },
215
+ ...(wireHistory.length > 0 ? { history: wireHistory } : {}),
216
+ },
217
+ ...(params.additionalModelRequestFields
218
+ ? { additionalModelRequestFields: params.additionalModelRequestFields }
219
+ : {}),
220
+ profileArn,
221
+ agentMode: "vibe",
222
+ },
223
+ wireHistoryLength: wireHistory.length,
224
+ wireContentLength: wireContent.length,
225
+ hasImages: !!currentImages,
226
+ toolResultCount: wireUimc?.toolResults?.length ?? 0,
227
+ };
228
+ }
229
+ //# sourceMappingURL=request-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-builder.js","sourceRoot":"","sources":["../src/request-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ import type { KiroModel } from "./models.js";
2
+ import type { KiroWireEventFrame } from "./response-stream.js";
3
+ import type { KiroStreamEvent, KiroUsage } from "./types.js";
4
+ /** What the finished attempt produced, before the caller decides to keep it. */
5
+ export interface KiroAttemptSummary {
6
+ responseText: string;
7
+ hasText: boolean;
8
+ sawAnyToolCalls: boolean;
9
+ emittedToolCalls: number;
10
+ /** The whole turn is a "Continue" echo taught by synthetic padding. */
11
+ isEchoLoop: boolean;
12
+ /** No text and no tool calls: a 200 that said nothing. */
13
+ isEmpty: boolean;
14
+ }
15
+ export interface KiroCompletedResponse {
16
+ stopReason: "stop" | "toolUse" | "length";
17
+ usage: KiroUsage;
18
+ }
19
+ /**
20
+ * Accumulates one response.
21
+ *
22
+ * Block indexes are monotonic across the whole call, including across an
23
+ * internal retry, so the buffer and the usage totals outlive a single attempt
24
+ * while everything else is reset by {@link beginAttempt}.
25
+ */
26
+ export declare class KiroResponseAssembler {
27
+ private readonly model;
28
+ private readonly thinkingEnabled;
29
+ private readonly pending;
30
+ private readonly blocks;
31
+ private readonly usage;
32
+ private totalContent;
33
+ private lastContentData;
34
+ private usageEvent;
35
+ private receivedContextUsage;
36
+ private thinkingParser;
37
+ private nativeThinkingBlockIndex;
38
+ private nativeThinkingEnded;
39
+ private textBlockIndex;
40
+ private emittedToolCalls;
41
+ private sawAnyToolCalls;
42
+ private currentToolCall;
43
+ constructor(model: KiroModel, thinkingEnabled: boolean);
44
+ /** Clear per-attempt state. Block indexes and accumulated usage are kept. */
45
+ beginAttempt(): void;
46
+ /** Hand over the events buffered so far. */
47
+ takeEvents(): KiroStreamEvent[];
48
+ /** Announce that a retry discarded everything emitted so far. */
49
+ discard(): void;
50
+ handle(frame: KiroWireEventFrame): void;
51
+ /**
52
+ * Close the turn: flush a trailing tool call, close thinking, and run the
53
+ * text-dialect recovery and echo-stripping passes.
54
+ */
55
+ endTurn(): KiroAttemptSummary;
56
+ /** Drop an echo the caller decided not to retry, so it is not read as a continuation signal. */
57
+ stripEcho(): void;
58
+ /** Close the text block and settle usage and the stop reason. */
59
+ complete(): KiroCompletedResponse;
60
+ private ensureNativeThinkingBlock;
61
+ private endNativeThinking;
62
+ private emitToolCall;
63
+ private flushToolCall;
64
+ /**
65
+ * Extract text-dialect tool calls from content when no native tool calls
66
+ * arrived. Two dialects are recovered at this seam:
67
+ * 1. Kiro's own `[Called name with args: {...}]` bracket form.
68
+ * 2. Anthropic's `<invoke name="..."><parameter .../></invoke>` XML form,
69
+ * which opus-class models emit as plain text at high context.
70
+ * Without this, the turn ends `stop` with zero tool calls — the agent loop
71
+ * sees a finished answer and an unattended session stalls with no error
72
+ * recorded anywhere.
73
+ *
74
+ * Models that emit native tool-use events opt out via
75
+ * `recoverTextToolCalls: false`. For them this pass has nothing to rescue
76
+ * and everything to break: prose that merely *quotes* the syntax — a model
77
+ * explaining how a tool is called — would be lifted into a real call the
78
+ * model never made. Absent means recover, so a model the catalog says
79
+ * nothing about keeps the fallback.
80
+ */
81
+ private recoverTextToolCalls;
82
+ /**
83
+ * Strip echo noise: when tool calls are present and the text content is just
84
+ * "." or a similar short echo from history padding, remove it. This prevents
85
+ * the echo from accumulating in conversation history and reinforcing the
86
+ * pattern in future turns.
87
+ */
88
+ private stripEchoNoise;
89
+ }