norn-cli 4.6.0 → 4.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +151 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -199700,6 +199700,9 @@ var AgentInvocationRunner = class {
|
|
|
199700
199700
|
inputTokenBudget;
|
|
199701
199701
|
invocationCount = 0;
|
|
199702
199702
|
toolResultStore = new AgentToolResultStore();
|
|
199703
|
+
rootInvocationId;
|
|
199704
|
+
journalEntryCount = 0;
|
|
199705
|
+
journalKeys = /* @__PURE__ */ new Map();
|
|
199703
199706
|
async run(agent, input2) {
|
|
199704
199707
|
throwIfSequenceCancelled(this.options.signal);
|
|
199705
199708
|
const rootInvocationIndex2 = this.options.rootInvocationIndex ?? 1;
|
|
@@ -199725,6 +199728,7 @@ var AgentInvocationRunner = class {
|
|
|
199725
199728
|
this.options.onInvocationActivity?.({
|
|
199726
199729
|
state,
|
|
199727
199730
|
invocationId: hop.invocationId,
|
|
199731
|
+
...this.rootInvocationId ? { rootInvocationId: this.rootInvocationId } : {},
|
|
199728
199732
|
...hop.parentInvocationId ? { parentInvocationId: hop.parentInvocationId } : {},
|
|
199729
199733
|
...hop.hopPath ? { hopPath: hop.hopPath } : {},
|
|
199730
199734
|
agent: hop.agent,
|
|
@@ -199733,6 +199737,39 @@ var AgentInvocationRunner = class {
|
|
|
199733
199737
|
...name ? { name } : {}
|
|
199734
199738
|
});
|
|
199735
199739
|
}
|
|
199740
|
+
reportJournalStatus(hop, input2) {
|
|
199741
|
+
const kind = input2.kind;
|
|
199742
|
+
const summary = typeof input2.summary === "string" ? input2.summary.trim() : "";
|
|
199743
|
+
const next = typeof input2.next === "string" ? input2.next.trim() : void 0;
|
|
199744
|
+
const kinds = ["new_work", "finding", "obstacle", "direction", "milestone"];
|
|
199745
|
+
if (!kinds.includes(kind) || !summary || summary.length > 280 || next !== void 0 && next.length > 160) {
|
|
199746
|
+
return { accepted: false, reason: "Use a listed kind, a 1\u2013280 character summary, and at most 160 characters for next." };
|
|
199747
|
+
}
|
|
199748
|
+
const keys = this.journalKeys.get(hop.invocationId) ?? /* @__PURE__ */ new Set();
|
|
199749
|
+
this.journalKeys.set(hop.invocationId, keys);
|
|
199750
|
+
const key = JSON.stringify([kind, summary, next ?? ""]);
|
|
199751
|
+
if (keys.has(key)) {
|
|
199752
|
+
return { accepted: true, reason: "Duplicate note omitted." };
|
|
199753
|
+
}
|
|
199754
|
+
if (keys.size >= 12 || this.journalEntryCount >= 48) {
|
|
199755
|
+
return { accepted: false, reason: "Journal limit reached. Continue the task without further status reports." };
|
|
199756
|
+
}
|
|
199757
|
+
keys.add(key);
|
|
199758
|
+
this.journalEntryCount++;
|
|
199759
|
+
if (this.rootInvocationId && this.options.onJournalEvent && !this.options.signal?.aborted) {
|
|
199760
|
+
this.options.onJournalEvent({
|
|
199761
|
+
type: "note",
|
|
199762
|
+
timestamp: Date.now(),
|
|
199763
|
+
rootInvocationId: this.rootInvocationId,
|
|
199764
|
+
invocationId: hop.invocationId,
|
|
199765
|
+
agent: hop.agent,
|
|
199766
|
+
kind,
|
|
199767
|
+
summary,
|
|
199768
|
+
...next ? { next } : {}
|
|
199769
|
+
});
|
|
199770
|
+
}
|
|
199771
|
+
return { accepted: true };
|
|
199772
|
+
}
|
|
199736
199773
|
validateContract(agent, kind, value) {
|
|
199737
199774
|
const builtIn = agent.builtInContracts?.[kind];
|
|
199738
199775
|
if (builtIn) {
|
|
@@ -199874,14 +199911,35 @@ var AgentInvocationRunner = class {
|
|
|
199874
199911
|
};
|
|
199875
199912
|
parentHop.output.toolCalls.push(record2);
|
|
199876
199913
|
const startedAt = Date.now();
|
|
199914
|
+
const journalCallId = definition.kind === "mcp" || definition.kind === "knowledge" ? this.nextInvocationId() : void 0;
|
|
199915
|
+
let toolFailed = false;
|
|
199916
|
+
if (journalCallId && this.rootInvocationId && this.options.onJournalEvent && !this.options.signal?.aborted) {
|
|
199917
|
+
this.options.onJournalEvent({
|
|
199918
|
+
type: "tool",
|
|
199919
|
+
timestamp: startedAt,
|
|
199920
|
+
rootInvocationId: this.rootInvocationId,
|
|
199921
|
+
invocationId: parentHop.invocationId,
|
|
199922
|
+
callId: journalCallId,
|
|
199923
|
+
agent: parentHop.agent,
|
|
199924
|
+
toolKind: definition.kind,
|
|
199925
|
+
name: definition.name,
|
|
199926
|
+
state: "started"
|
|
199927
|
+
});
|
|
199928
|
+
}
|
|
199877
199929
|
const activityKind = definition.kind === "agent" ? "agent" : definition.kind === "knowledge" ? "knowledge" : "mcp";
|
|
199878
|
-
const reportsActivity = definition.kind !== "context";
|
|
199930
|
+
const reportsActivity = definition.kind !== "context" && definition.kind !== "journal";
|
|
199879
199931
|
if (reportsActivity) {
|
|
199880
199932
|
this.emitActivity(parentHop, "active", activityKind, definition.name);
|
|
199881
199933
|
}
|
|
199882
199934
|
try {
|
|
199883
199935
|
const output2 = await execute(input2, context);
|
|
199936
|
+
if (output2 && typeof output2 === "object" && "isError" in output2 && output2.isError === true) {
|
|
199937
|
+
toolFailed = true;
|
|
199938
|
+
}
|
|
199884
199939
|
record2.output = cloneSnapshotValue(output2, null);
|
|
199940
|
+
if (definition.kind === "journal") {
|
|
199941
|
+
return output2;
|
|
199942
|
+
}
|
|
199885
199943
|
if (definition.kind === "context") {
|
|
199886
199944
|
const text = agentInputToText(output2);
|
|
199887
199945
|
return new PreparedAgentToolResult(output2, output2, text, JSON.stringify({
|
|
@@ -199899,10 +199957,25 @@ var AgentInvocationRunner = class {
|
|
|
199899
199957
|
ownerId: parentHop.invocationId
|
|
199900
199958
|
});
|
|
199901
199959
|
} catch (error2) {
|
|
199960
|
+
toolFailed = true;
|
|
199902
199961
|
record2.output = getToolErrorOutput(error2);
|
|
199903
199962
|
throw error2;
|
|
199904
199963
|
} finally {
|
|
199905
199964
|
record2.ms = context.recordedMs ?? Date.now() - startedAt;
|
|
199965
|
+
if (journalCallId && this.rootInvocationId && this.options.onJournalEvent && !this.options.signal?.aborted) {
|
|
199966
|
+
this.options.onJournalEvent({
|
|
199967
|
+
type: "tool",
|
|
199968
|
+
timestamp: Date.now(),
|
|
199969
|
+
rootInvocationId: this.rootInvocationId,
|
|
199970
|
+
invocationId: parentHop.invocationId,
|
|
199971
|
+
callId: journalCallId,
|
|
199972
|
+
agent: parentHop.agent,
|
|
199973
|
+
toolKind: definition.kind,
|
|
199974
|
+
name: definition.name,
|
|
199975
|
+
state: "completed",
|
|
199976
|
+
...toolFailed ? { failed: true } : {}
|
|
199977
|
+
});
|
|
199978
|
+
}
|
|
199906
199979
|
if (context.childInvocationId) {
|
|
199907
199980
|
record2.childInvocationId = context.childInvocationId;
|
|
199908
199981
|
}
|
|
@@ -199942,10 +200015,13 @@ var AgentInvocationRunner = class {
|
|
|
199942
200015
|
this.options.onInvocationProgress?.({ phase: "completed", hop });
|
|
199943
200016
|
return hop;
|
|
199944
200017
|
}
|
|
199945
|
-
async invokeAgent(agent, input2, depth, parentInvocationId, ancestors, conversation, hopPath) {
|
|
200018
|
+
async invokeAgent(agent, input2, depth, parentInvocationId, ancestors, conversation, hopPath, parentAgentName) {
|
|
199946
200019
|
throwIfSequenceCancelled(this.options.signal);
|
|
199947
200020
|
const startedAt = Date.now();
|
|
199948
200021
|
const invocationId = this.nextInvocationId();
|
|
200022
|
+
if (depth === 0) {
|
|
200023
|
+
this.rootInvocationId = invocationId;
|
|
200024
|
+
}
|
|
199949
200025
|
const hop = {
|
|
199950
200026
|
invocationId,
|
|
199951
200027
|
...parentInvocationId ? { parentInvocationId } : {},
|
|
@@ -199971,6 +200047,18 @@ var AgentInvocationRunner = class {
|
|
|
199971
200047
|
depth,
|
|
199972
200048
|
cycle: hop.cycle
|
|
199973
200049
|
});
|
|
200050
|
+
if (parentInvocationId && parentAgentName && this.rootInvocationId && this.options.onJournalEvent && !this.options.signal?.aborted) {
|
|
200051
|
+
this.options.onJournalEvent({
|
|
200052
|
+
type: "handoff",
|
|
200053
|
+
timestamp: Date.now(),
|
|
200054
|
+
rootInvocationId: this.rootInvocationId,
|
|
200055
|
+
invocationId,
|
|
200056
|
+
parentInvocationId,
|
|
200057
|
+
parentAgent: parentAgentName,
|
|
200058
|
+
agent: agent.name,
|
|
200059
|
+
input: input2
|
|
200060
|
+
});
|
|
200061
|
+
}
|
|
199974
200062
|
this.emitActivity(hop, "active", "preparing");
|
|
199975
200063
|
let resolvedModel;
|
|
199976
200064
|
let fatalToolError;
|
|
@@ -200031,6 +200119,9 @@ var AgentInvocationRunner = class {
|
|
|
200031
200119
|
invocationId
|
|
200032
200120
|
);
|
|
200033
200121
|
let systemPrompt = preparedKnowledge?.assembly.systemPrompt ?? authoredSystemPrompt;
|
|
200122
|
+
if (this.options.onJournalEvent && !requestOverride) {
|
|
200123
|
+
systemPrompt += "\n\nDevelopment work journal: call report_progress only when starting a distinct piece of work, finding something that changes the plan, encountering an obstacle, changing direction, or finishing a milestone. Keep each note short and factual about your own observed work. Do not report every tool call or use it as a heartbeat. Each report uses a model/tool turn and counts toward max_turns.";
|
|
200124
|
+
}
|
|
200034
200125
|
hop.accepts = this.validateContract(agent, "accepts", input2);
|
|
200035
200126
|
if (hop.accepts && !hop.accepts.assertion.passed) {
|
|
200036
200127
|
throw new Error(contractFailureMessage(hop.accepts));
|
|
@@ -200205,6 +200296,16 @@ ${TOOL_RESULT_RECOVERY_INSTRUCTIONS}`;
|
|
|
200205
200296
|
if (reportedError instanceof AgentProviderRefusalError) {
|
|
200206
200297
|
hop.output.text = reportedError.rawText;
|
|
200207
200298
|
}
|
|
200299
|
+
if (hop.error && this.rootInvocationId && this.options.onJournalEvent) {
|
|
200300
|
+
this.options.onJournalEvent({
|
|
200301
|
+
type: "runtime",
|
|
200302
|
+
timestamp: Date.now(),
|
|
200303
|
+
rootInvocationId: this.rootInvocationId,
|
|
200304
|
+
invocationId,
|
|
200305
|
+
agent: agent.name,
|
|
200306
|
+
summary: `${agent.name} stopped: ${hop.error.slice(0, 280)}`
|
|
200307
|
+
});
|
|
200308
|
+
}
|
|
200208
200309
|
} finally {
|
|
200209
200310
|
await this.options.mcpSessionManager?.closeScope(invocationId);
|
|
200210
200311
|
if (this.options.signal?.aborted) {
|
|
@@ -200255,9 +200356,13 @@ ${TOOL_RESULT_RECOVERY_INSTRUCTIONS}`;
|
|
|
200255
200356
|
overrideKeys.add(key);
|
|
200256
200357
|
}
|
|
200257
200358
|
let callerLimitBreaches = 0;
|
|
200258
|
-
|
|
200359
|
+
const reservedAgentNames = ["load_knowledge", "norn_read_tool_result", "norn_search_tool_results"];
|
|
200360
|
+
if (this.options.onJournalEvent || requestToolsOverride?.some((tool) => tool.kind === "journal")) {
|
|
200361
|
+
reservedAgentNames.push("report_progress");
|
|
200362
|
+
}
|
|
200363
|
+
if (agent.callableAgents.some((reference) => reservedAgentNames.includes(reference.name.toLowerCase()))) {
|
|
200259
200364
|
throw new Error(
|
|
200260
|
-
|
|
200365
|
+
`Agent names ${reservedAgentNames.map((name) => `'${name}'`).join(", ")} are reserved by Norn.`
|
|
200261
200366
|
);
|
|
200262
200367
|
}
|
|
200263
200368
|
if (knowledge?.hasLoadableKnowledge) {
|
|
@@ -200504,7 +200609,8 @@ ${TOOL_RESULT_RECOVERY_INSTRUCTIONS}`;
|
|
|
200504
200609
|
parentHop.invocationId,
|
|
200505
200610
|
ancestors,
|
|
200506
200611
|
[...conversation],
|
|
200507
|
-
childHopPath
|
|
200612
|
+
childHopPath,
|
|
200613
|
+
parentHop.agent
|
|
200508
200614
|
);
|
|
200509
200615
|
toolContext.childInvocationId = child.invocationId;
|
|
200510
200616
|
for (const retry2 of pendingInputRetries) {
|
|
@@ -200563,8 +200669,28 @@ ${TOOL_RESULT_RECOVERY_INSTRUCTIONS}`;
|
|
|
200563
200669
|
return Object.prototype.hasOwnProperty.call(child.output, "body") ? child.output.body : child.output.text;
|
|
200564
200670
|
}));
|
|
200565
200671
|
}
|
|
200672
|
+
const offeredJournal = requestToolsOverride?.find((tool) => tool.kind === "journal" && tool.name === "report_progress");
|
|
200673
|
+
if (this.options.onJournalEvent || offeredJournal) {
|
|
200674
|
+
providerTools.push(this.createRecordedProviderTool(parentHop, {
|
|
200675
|
+
name: "report_progress",
|
|
200676
|
+
providerName: offeredJournal?.providerName ?? "norn_report_progress",
|
|
200677
|
+
kind: "journal",
|
|
200678
|
+
description: offeredJournal?.description ?? "Record a short, meaningful development work-journal note. Use sparingly for a new piece of work, finding, obstacle, change of direction, or milestone. Do not report routine tool calls. This call uses one of your allowed model/tool turns.",
|
|
200679
|
+
inputSchema: offeredJournal?.inputSchema ?? {
|
|
200680
|
+
type: "object",
|
|
200681
|
+
properties: {
|
|
200682
|
+
kind: { type: "string", enum: ["new_work", "finding", "obstacle", "direction", "milestone"] },
|
|
200683
|
+
summary: { type: "string", minLength: 1, maxLength: 280 },
|
|
200684
|
+
next: { type: "string", maxLength: 160 }
|
|
200685
|
+
},
|
|
200686
|
+
required: ["kind", "summary"],
|
|
200687
|
+
additionalProperties: false
|
|
200688
|
+
}
|
|
200689
|
+
}, async (input2) => this.reportJournalStatus(parentHop, input2)));
|
|
200690
|
+
}
|
|
200691
|
+
const hasRecoverableTools = providerTools.some((tool) => tool.kind !== "journal");
|
|
200566
200692
|
const offeredContext = requestToolsOverride?.find((tool) => tool.name === "norn_read_tool_result" && tool.kind === "context");
|
|
200567
|
-
if (!requestToolsOverride &&
|
|
200693
|
+
if (!requestToolsOverride && hasRecoverableTools || offeredContext) {
|
|
200568
200694
|
providerTools.push(this.createRecordedProviderTool(parentHop, {
|
|
200569
200695
|
name: "norn_read_tool_result",
|
|
200570
200696
|
providerName: offeredContext?.providerName ?? "norn_read_tool_result",
|
|
@@ -200583,7 +200709,7 @@ ${TOOL_RESULT_RECOVERY_INSTRUCTIONS}`;
|
|
|
200583
200709
|
}, async (input2) => this.toolResultStore.read(input2, parentHop.invocationId)));
|
|
200584
200710
|
}
|
|
200585
200711
|
const offeredSearch = requestToolsOverride?.find((tool) => tool.name === "norn_search_tool_results" && tool.kind === "context");
|
|
200586
|
-
if (!requestToolsOverride &&
|
|
200712
|
+
if (!requestToolsOverride && hasRecoverableTools || offeredSearch) {
|
|
200587
200713
|
providerTools.push(this.createRecordedProviderTool(parentHop, {
|
|
200588
200714
|
name: "norn_search_tool_results",
|
|
200589
200715
|
providerName: offeredSearch?.providerName ?? "norn_search_tool_results",
|
|
@@ -201927,7 +202053,7 @@ function isSafeProjectRelativePath(value) {
|
|
|
201927
202053
|
return isRelativePath(value) && !normalizePathSeparators(value).split("/").includes("..");
|
|
201928
202054
|
}
|
|
201929
202055
|
function isAgentToolCall(value) {
|
|
201930
|
-
return isObjectRecord2(value) && typeof value.name === "string" && (value.kind === void 0 || value.kind === "mcp" || value.kind === "agent" || value.kind === "knowledge" || value.kind === "context") && isObjectRecord2(value.input) && isFiniteNonNegativeNumber(value.ms) && (value.childInvocationId === void 0 || typeof value.childInvocationId === "string");
|
|
202056
|
+
return isObjectRecord2(value) && typeof value.name === "string" && (value.kind === void 0 || value.kind === "mcp" || value.kind === "agent" || value.kind === "knowledge" || value.kind === "context" || value.kind === "journal") && isObjectRecord2(value.input) && isFiniteNonNegativeNumber(value.ms) && (value.childInvocationId === void 0 || typeof value.childInvocationId === "string");
|
|
201931
202057
|
}
|
|
201932
202058
|
function isAgentKnowledgeTopicSnapshot(value) {
|
|
201933
202059
|
return isObjectRecord2(value) && typeof value.topicHash === "string" && (value.heading === void 0 || typeof value.heading === "string") && isFiniteNonNegativeNumber(value.estimatedTokens) && isStringArray(value.itemPaths);
|
|
@@ -201943,7 +202069,7 @@ function isAgentRequestSnapshot(value) {
|
|
|
201943
202069
|
(message) => isObjectRecord2(message) && (message.role === "user" || message.role === "assistant") && typeof message.content === "string"
|
|
201944
202070
|
);
|
|
201945
202071
|
const validTools = value.tools.every(
|
|
201946
|
-
(tool) => isObjectRecord2(tool) && typeof tool.name === "string" && typeof tool.providerName === "string" && (tool.kind === void 0 || tool.kind === "mcp" || tool.kind === "agent" || tool.kind === "knowledge" || tool.kind === "context") && (tool.description === void 0 || typeof tool.description === "string") && isObjectRecord2(tool.inputSchema)
|
|
202072
|
+
(tool) => isObjectRecord2(tool) && typeof tool.name === "string" && typeof tool.providerName === "string" && (tool.kind === void 0 || tool.kind === "mcp" || tool.kind === "agent" || tool.kind === "knowledge" || tool.kind === "context" || tool.kind === "journal") && (tool.description === void 0 || typeof tool.description === "string") && isObjectRecord2(tool.inputSchema)
|
|
201947
202073
|
);
|
|
201948
202074
|
const validKnowledge = value.knowledge === void 0 || isObjectRecord2(value.knowledge) && typeof value.knowledge.runId === "string" && typeof value.knowledge.snapshotId === "string" && isStringArray(value.knowledge.declarationIds) && ["exact_entity", "lexical_unique", "lexical_ambiguous", "no_match"].includes(
|
|
201949
202075
|
String(value.knowledge.routingRule)
|
|
@@ -207581,7 +207707,19 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
|
|
|
207581
207707
|
nestingDepth,
|
|
207582
207708
|
agentActivity
|
|
207583
207709
|
});
|
|
207584
|
-
}
|
|
207710
|
+
},
|
|
207711
|
+
...executionContext?.agentWorkJournal ? {
|
|
207712
|
+
onJournalEvent: (agentJournal) => {
|
|
207713
|
+
onProgress?.({
|
|
207714
|
+
currentStep: stepIdx + 1,
|
|
207715
|
+
totalSteps,
|
|
207716
|
+
stepType: "agent",
|
|
207717
|
+
stepDescription: `${agentJournal.agent} work journal`,
|
|
207718
|
+
nestingDepth,
|
|
207719
|
+
agentJournal
|
|
207720
|
+
});
|
|
207721
|
+
}
|
|
207722
|
+
} : {}
|
|
207585
207723
|
});
|
|
207586
207724
|
const run = await runner.run(agent, input2);
|
|
207587
207725
|
hop = run.hop;
|
|
@@ -208295,6 +208433,7 @@ ${indentMultiline(userMessage)}`;
|
|
|
208295
208433
|
agentEventLog,
|
|
208296
208434
|
agentRootInvocationCounts,
|
|
208297
208435
|
recordAgentRuns: executionContext?.recordAgentRuns,
|
|
208436
|
+
agentWorkJournal: executionContext?.agentWorkJournal,
|
|
208298
208437
|
agentRecordingOutputPath: executionContext?.agentRecordingOutputPath,
|
|
208299
208438
|
knowledgeCoordinator,
|
|
208300
208439
|
signal: executionContext?.signal
|
|
@@ -209528,7 +209667,7 @@ function formatAgentHopAtDepth(hop, options, displayDepth) {
|
|
|
209528
209667
|
const renderedChildren = /* @__PURE__ */ new Set();
|
|
209529
209668
|
const childrenByInvocationId = new Map(hop.children.map((child) => [child.invocationId, child]));
|
|
209530
209669
|
for (const toolCall of hop.output.toolCalls) {
|
|
209531
|
-
if (toolCall.kind === "context" || toolCall.kind === "knowledge" && isKnowledgeAlreadyLoaded(toolCall.output)) {
|
|
209670
|
+
if (toolCall.kind === "context" || toolCall.kind === "journal" || toolCall.kind === "knowledge" && isKnowledgeAlreadyLoaded(toolCall.output)) {
|
|
209532
209671
|
continue;
|
|
209533
209672
|
}
|
|
209534
209673
|
if (toolCall.kind === "agent" || toolCall.childInvocationId) {
|
|
@@ -214683,7 +214822,7 @@ async function main() {
|
|
|
214683
214822
|
}
|
|
214684
214823
|
const args = rawArgs[0] === "run" ? rawArgs.slice(1) : rawArgs;
|
|
214685
214824
|
if (args.length === 1 && (args[0] === "--version" || args[0] === "-V")) {
|
|
214686
|
-
console.log(`Norn CLI ${"4.
|
|
214825
|
+
console.log(`Norn CLI ${"4.6.0"}`);
|
|
214687
214826
|
return;
|
|
214688
214827
|
}
|
|
214689
214828
|
if (args.length === 0) {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "norn-cli",
|
|
3
3
|
"displayName": "Norn",
|
|
4
4
|
"description": "Build version-controlled agentic flows in VS Code and run them from the CLI or CI.",
|
|
5
|
-
"version": "4.6.
|
|
5
|
+
"version": "4.6.1",
|
|
6
6
|
"publisher": "Norn-PeterKrustanov",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Peter Krastanov"
|