norn-cli 4.6.0 → 4.6.2
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 +251 -22
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -166647,6 +166647,9 @@ function parseDurationToMs(rawValue, defaultUnit = "ms") {
|
|
|
166647
166647
|
}
|
|
166648
166648
|
|
|
166649
166649
|
// src/formUrlEncoded.ts
|
|
166650
|
+
function decodeFieldValue(value) {
|
|
166651
|
+
return isQuotedStringLiteral(value) ? decodeQuotedStringLiteral(value) : value;
|
|
166652
|
+
}
|
|
166650
166653
|
function parseEqualsField(segment) {
|
|
166651
166654
|
const eqIndex = segment.indexOf("=");
|
|
166652
166655
|
if (eqIndex <= 0) {
|
|
@@ -166654,9 +166657,53 @@ function parseEqualsField(segment) {
|
|
|
166654
166657
|
}
|
|
166655
166658
|
return {
|
|
166656
166659
|
key: segment.substring(0, eqIndex).trim(),
|
|
166657
|
-
value: segment.substring(eqIndex + 1).trim()
|
|
166660
|
+
value: decodeFieldValue(segment.substring(eqIndex + 1).trim())
|
|
166658
166661
|
};
|
|
166659
166662
|
}
|
|
166663
|
+
function splitAmpersandDelimitedLine(line2) {
|
|
166664
|
+
const segments = [];
|
|
166665
|
+
let current = "";
|
|
166666
|
+
let quote;
|
|
166667
|
+
let escaped = false;
|
|
166668
|
+
let inValue = false;
|
|
166669
|
+
let valueStarted = false;
|
|
166670
|
+
for (const char of line2) {
|
|
166671
|
+
if (escaped) {
|
|
166672
|
+
current += char;
|
|
166673
|
+
escaped = false;
|
|
166674
|
+
continue;
|
|
166675
|
+
}
|
|
166676
|
+
if (quote && char === "\\") {
|
|
166677
|
+
current += char;
|
|
166678
|
+
escaped = true;
|
|
166679
|
+
continue;
|
|
166680
|
+
}
|
|
166681
|
+
if ((char === '"' || char === "'") && (quote || inValue && !valueStarted)) {
|
|
166682
|
+
if (!quote) {
|
|
166683
|
+
quote = char;
|
|
166684
|
+
} else if (quote === char) {
|
|
166685
|
+
quote = void 0;
|
|
166686
|
+
}
|
|
166687
|
+
current += char;
|
|
166688
|
+
continue;
|
|
166689
|
+
}
|
|
166690
|
+
if (char === "&" && !quote) {
|
|
166691
|
+
segments.push(current.trim());
|
|
166692
|
+
current = "";
|
|
166693
|
+
inValue = false;
|
|
166694
|
+
valueStarted = false;
|
|
166695
|
+
continue;
|
|
166696
|
+
}
|
|
166697
|
+
current += char;
|
|
166698
|
+
if (!inValue && char === "=") {
|
|
166699
|
+
inValue = true;
|
|
166700
|
+
} else if (inValue && !/\s/.test(char)) {
|
|
166701
|
+
valueStarted = true;
|
|
166702
|
+
}
|
|
166703
|
+
}
|
|
166704
|
+
segments.push(current.trim());
|
|
166705
|
+
return segments;
|
|
166706
|
+
}
|
|
166660
166707
|
function isFormUrlEncodedContentType(contentType) {
|
|
166661
166708
|
return Boolean(contentType && contentType.toLowerCase().includes("application/x-www-form-urlencoded"));
|
|
166662
166709
|
}
|
|
@@ -166672,7 +166719,7 @@ function parseFormUrlEncodedLines(lines) {
|
|
|
166672
166719
|
const firstColonIndex = line2.indexOf(":");
|
|
166673
166720
|
if (firstEqIndex > 0 && (firstColonIndex === -1 || firstEqIndex < firstColonIndex)) {
|
|
166674
166721
|
if (line2.includes("&")) {
|
|
166675
|
-
const segments = line2
|
|
166722
|
+
const segments = splitAmpersandDelimitedLine(line2);
|
|
166676
166723
|
const hasInvalidSegment = segments.some((segment) => !parseEqualsField(segment));
|
|
166677
166724
|
if (hasInvalidSegment) {
|
|
166678
166725
|
errors.push({
|
|
@@ -166699,7 +166746,7 @@ function parseFormUrlEncodedLines(lines) {
|
|
|
166699
166746
|
if (firstColonIndex > 0) {
|
|
166700
166747
|
fields.push({
|
|
166701
166748
|
key: line2.substring(0, firstColonIndex).trim(),
|
|
166702
|
-
value: line2.substring(firstColonIndex + 1).trim()
|
|
166749
|
+
value: decodeFieldValue(line2.substring(firstColonIndex + 1).trim())
|
|
166703
166750
|
});
|
|
166704
166751
|
continue;
|
|
166705
166752
|
}
|
|
@@ -166723,10 +166770,13 @@ function encodeFormUrlEncodedBody(body) {
|
|
|
166723
166770
|
return { encodedBody: "", errors };
|
|
166724
166771
|
}
|
|
166725
166772
|
return {
|
|
166726
|
-
encodedBody: fields
|
|
166773
|
+
encodedBody: encodeFormUrlEncodedFields(fields),
|
|
166727
166774
|
errors: []
|
|
166728
166775
|
};
|
|
166729
166776
|
}
|
|
166777
|
+
function encodeFormUrlEncodedFields(fields) {
|
|
166778
|
+
return fields.map((field) => `${encodeURIComponent(field.key)}=${encodeURIComponent(field.value)}`).join("&");
|
|
166779
|
+
}
|
|
166730
166780
|
|
|
166731
166781
|
// src/sequenceCancellation.ts
|
|
166732
166782
|
var SEQUENCE_CANCELLED_MESSAGE = "Sequence run cancelled by user.";
|
|
@@ -166861,7 +166911,10 @@ async function sendRequestWithJar(request, jar, retryOptions, signal) {
|
|
|
166861
166911
|
);
|
|
166862
166912
|
const contentTypeValue = contentType ? headers[contentType] : "";
|
|
166863
166913
|
if (isFormUrlEncodedContentType(contentTypeValue)) {
|
|
166864
|
-
const encoded =
|
|
166914
|
+
const encoded = request.formUrlEncodedBody ? {
|
|
166915
|
+
encodedBody: encodeFormUrlEncodedFields(request.formUrlEncodedBody.fields),
|
|
166916
|
+
errors: request.formUrlEncodedBody.errors
|
|
166917
|
+
} : encodeFormUrlEncodedBody(currentBody);
|
|
166865
166918
|
if (encoded.errors.length > 0) {
|
|
166866
166919
|
throw new NornError({
|
|
166867
166920
|
category: "syntax",
|
|
@@ -168154,7 +168207,7 @@ function validatePreparedRequest(parsed, context) {
|
|
|
168154
168207
|
}
|
|
168155
168208
|
const contentType = getHeaderValueCaseInsensitive(parsed.headers, "Content-Type");
|
|
168156
168209
|
if (isFormUrlEncodedContentType(contentType)) {
|
|
168157
|
-
const
|
|
168210
|
+
const errors = parsed.formUrlEncodedBody?.errors ?? parseFormUrlEncodedBody(parsed.body).errors;
|
|
168158
168211
|
if (errors.length > 0) {
|
|
168159
168212
|
throw new NornError({
|
|
168160
168213
|
category: "syntax",
|
|
@@ -185378,6 +185431,25 @@ var McpSessionManager = class {
|
|
|
185378
185431
|
};
|
|
185379
185432
|
|
|
185380
185433
|
// src/requestPreparation.ts
|
|
185434
|
+
function getHeaderValueCaseInsensitive2(headers, name) {
|
|
185435
|
+
const target = name.toLowerCase();
|
|
185436
|
+
return Object.entries(headers).find(([headerName]) => headerName.toLowerCase() === target)?.[1];
|
|
185437
|
+
}
|
|
185438
|
+
function attachPreparedFormBody(request, authoredBody, variables) {
|
|
185439
|
+
const contentType = getHeaderValueCaseInsensitive2(request.headers, "Content-Type");
|
|
185440
|
+
if (!isFormUrlEncodedContentType(contentType) || !authoredBody) {
|
|
185441
|
+
return request;
|
|
185442
|
+
}
|
|
185443
|
+
const parsed = parseFormUrlEncodedBody(authoredBody);
|
|
185444
|
+
const prepared = {
|
|
185445
|
+
errors: parsed.errors,
|
|
185446
|
+
fields: parsed.fields.map((field) => ({
|
|
185447
|
+
key: substituteVariables(field.key, variables),
|
|
185448
|
+
value: substituteVariables(field.value, variables)
|
|
185449
|
+
}))
|
|
185450
|
+
};
|
|
185451
|
+
return { ...request, formUrlEncodedBody: prepared };
|
|
185452
|
+
}
|
|
185381
185453
|
function escapeRegExp(value) {
|
|
185382
185454
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
185383
185455
|
}
|
|
@@ -185421,7 +185493,8 @@ function resolveBareVariableRequestBody(request, variables) {
|
|
|
185421
185493
|
}
|
|
185422
185494
|
return {
|
|
185423
185495
|
...request,
|
|
185424
|
-
body: stringifyRequestValue(variables[bareVariableMatch[1]])
|
|
185496
|
+
body: stringifyRequestValue(variables[bareVariableMatch[1]]),
|
|
185497
|
+
formUrlEncodedBody: void 0
|
|
185425
185498
|
};
|
|
185426
185499
|
}
|
|
185427
185500
|
function applyHeaderGroupsToRequest(parsed, requestText, headerGroups, variables) {
|
|
@@ -185530,12 +185603,13 @@ function prepareRequestFromContent(requestContent, variables, apiDefinitions) {
|
|
|
185530
185603
|
for (const [headerName, headerValue] of Object.entries(apiRequest.inlineHeaders)) {
|
|
185531
185604
|
combinedHeaders[headerName] = substituteVariables(headerValue, variables);
|
|
185532
185605
|
}
|
|
185533
|
-
|
|
185606
|
+
let request2 = {
|
|
185534
185607
|
method: apiRequest.method,
|
|
185535
185608
|
url: resolvedPath,
|
|
185536
185609
|
headers: combinedHeaders,
|
|
185537
185610
|
body: apiRequest.body ? substituteVariables(apiRequest.body, variables) : void 0
|
|
185538
185611
|
};
|
|
185612
|
+
request2 = attachPreparedFormBody(request2, apiRequest.body, variables);
|
|
185539
185613
|
return {
|
|
185540
185614
|
request: request2,
|
|
185541
185615
|
description: `${apiRequest.endpointName}(${Object.values(resolvedParams).join(", ")}) -> ${request2.method} ${request2.url}`,
|
|
@@ -185544,9 +185618,12 @@ function prepareRequestFromContent(requestContent, variables, apiDefinitions) {
|
|
|
185544
185618
|
}
|
|
185545
185619
|
}
|
|
185546
185620
|
let request = parserHttpRequest(requestContent, variables);
|
|
185621
|
+
let authoredRequest = parserHttpRequest(requestContent, {});
|
|
185547
185622
|
if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
|
|
185548
185623
|
request = applyHeaderGroupsToRequest(request, requestContent, apiDefinitions.headerGroups, variables);
|
|
185624
|
+
authoredRequest = applyHeaderGroupsToRequest(authoredRequest, requestContent, apiDefinitions.headerGroups, {});
|
|
185549
185625
|
}
|
|
185626
|
+
request = attachPreparedFormBody(request, authoredRequest.body, variables);
|
|
185550
185627
|
return {
|
|
185551
185628
|
request,
|
|
185552
185629
|
description: `${request.method} ${request.url}`
|
|
@@ -199700,6 +199777,9 @@ var AgentInvocationRunner = class {
|
|
|
199700
199777
|
inputTokenBudget;
|
|
199701
199778
|
invocationCount = 0;
|
|
199702
199779
|
toolResultStore = new AgentToolResultStore();
|
|
199780
|
+
rootInvocationId;
|
|
199781
|
+
journalEntryCount = 0;
|
|
199782
|
+
journalKeys = /* @__PURE__ */ new Map();
|
|
199703
199783
|
async run(agent, input2) {
|
|
199704
199784
|
throwIfSequenceCancelled(this.options.signal);
|
|
199705
199785
|
const rootInvocationIndex2 = this.options.rootInvocationIndex ?? 1;
|
|
@@ -199725,6 +199805,7 @@ var AgentInvocationRunner = class {
|
|
|
199725
199805
|
this.options.onInvocationActivity?.({
|
|
199726
199806
|
state,
|
|
199727
199807
|
invocationId: hop.invocationId,
|
|
199808
|
+
...this.rootInvocationId ? { rootInvocationId: this.rootInvocationId } : {},
|
|
199728
199809
|
...hop.parentInvocationId ? { parentInvocationId: hop.parentInvocationId } : {},
|
|
199729
199810
|
...hop.hopPath ? { hopPath: hop.hopPath } : {},
|
|
199730
199811
|
agent: hop.agent,
|
|
@@ -199733,6 +199814,39 @@ var AgentInvocationRunner = class {
|
|
|
199733
199814
|
...name ? { name } : {}
|
|
199734
199815
|
});
|
|
199735
199816
|
}
|
|
199817
|
+
reportJournalStatus(hop, input2) {
|
|
199818
|
+
const kind = input2.kind;
|
|
199819
|
+
const summary = typeof input2.summary === "string" ? input2.summary.trim() : "";
|
|
199820
|
+
const next = typeof input2.next === "string" ? input2.next.trim() : void 0;
|
|
199821
|
+
const kinds = ["new_work", "finding", "obstacle", "direction", "milestone"];
|
|
199822
|
+
if (!kinds.includes(kind) || !summary || summary.length > 280 || next !== void 0 && next.length > 160) {
|
|
199823
|
+
return { accepted: false, reason: "Use a listed kind, a 1\u2013280 character summary, and at most 160 characters for next." };
|
|
199824
|
+
}
|
|
199825
|
+
const keys = this.journalKeys.get(hop.invocationId) ?? /* @__PURE__ */ new Set();
|
|
199826
|
+
this.journalKeys.set(hop.invocationId, keys);
|
|
199827
|
+
const key = JSON.stringify([kind, summary, next ?? ""]);
|
|
199828
|
+
if (keys.has(key)) {
|
|
199829
|
+
return { accepted: true, reason: "Duplicate note omitted." };
|
|
199830
|
+
}
|
|
199831
|
+
if (keys.size >= 12 || this.journalEntryCount >= 48) {
|
|
199832
|
+
return { accepted: false, reason: "Journal limit reached. Continue the task without further status reports." };
|
|
199833
|
+
}
|
|
199834
|
+
keys.add(key);
|
|
199835
|
+
this.journalEntryCount++;
|
|
199836
|
+
if (this.rootInvocationId && this.options.onJournalEvent && !this.options.signal?.aborted) {
|
|
199837
|
+
this.options.onJournalEvent({
|
|
199838
|
+
type: "note",
|
|
199839
|
+
timestamp: Date.now(),
|
|
199840
|
+
rootInvocationId: this.rootInvocationId,
|
|
199841
|
+
invocationId: hop.invocationId,
|
|
199842
|
+
agent: hop.agent,
|
|
199843
|
+
kind,
|
|
199844
|
+
summary,
|
|
199845
|
+
...next ? { next } : {}
|
|
199846
|
+
});
|
|
199847
|
+
}
|
|
199848
|
+
return { accepted: true };
|
|
199849
|
+
}
|
|
199736
199850
|
validateContract(agent, kind, value) {
|
|
199737
199851
|
const builtIn = agent.builtInContracts?.[kind];
|
|
199738
199852
|
if (builtIn) {
|
|
@@ -199874,14 +199988,35 @@ var AgentInvocationRunner = class {
|
|
|
199874
199988
|
};
|
|
199875
199989
|
parentHop.output.toolCalls.push(record2);
|
|
199876
199990
|
const startedAt = Date.now();
|
|
199991
|
+
const journalCallId = definition.kind === "mcp" || definition.kind === "knowledge" ? this.nextInvocationId() : void 0;
|
|
199992
|
+
let toolFailed = false;
|
|
199993
|
+
if (journalCallId && this.rootInvocationId && this.options.onJournalEvent && !this.options.signal?.aborted) {
|
|
199994
|
+
this.options.onJournalEvent({
|
|
199995
|
+
type: "tool",
|
|
199996
|
+
timestamp: startedAt,
|
|
199997
|
+
rootInvocationId: this.rootInvocationId,
|
|
199998
|
+
invocationId: parentHop.invocationId,
|
|
199999
|
+
callId: journalCallId,
|
|
200000
|
+
agent: parentHop.agent,
|
|
200001
|
+
toolKind: definition.kind,
|
|
200002
|
+
name: definition.name,
|
|
200003
|
+
state: "started"
|
|
200004
|
+
});
|
|
200005
|
+
}
|
|
199877
200006
|
const activityKind = definition.kind === "agent" ? "agent" : definition.kind === "knowledge" ? "knowledge" : "mcp";
|
|
199878
|
-
const reportsActivity = definition.kind !== "context";
|
|
200007
|
+
const reportsActivity = definition.kind !== "context" && definition.kind !== "journal";
|
|
199879
200008
|
if (reportsActivity) {
|
|
199880
200009
|
this.emitActivity(parentHop, "active", activityKind, definition.name);
|
|
199881
200010
|
}
|
|
199882
200011
|
try {
|
|
199883
200012
|
const output2 = await execute(input2, context);
|
|
200013
|
+
if (output2 && typeof output2 === "object" && "isError" in output2 && output2.isError === true) {
|
|
200014
|
+
toolFailed = true;
|
|
200015
|
+
}
|
|
199884
200016
|
record2.output = cloneSnapshotValue(output2, null);
|
|
200017
|
+
if (definition.kind === "journal") {
|
|
200018
|
+
return output2;
|
|
200019
|
+
}
|
|
199885
200020
|
if (definition.kind === "context") {
|
|
199886
200021
|
const text = agentInputToText(output2);
|
|
199887
200022
|
return new PreparedAgentToolResult(output2, output2, text, JSON.stringify({
|
|
@@ -199899,10 +200034,25 @@ var AgentInvocationRunner = class {
|
|
|
199899
200034
|
ownerId: parentHop.invocationId
|
|
199900
200035
|
});
|
|
199901
200036
|
} catch (error2) {
|
|
200037
|
+
toolFailed = true;
|
|
199902
200038
|
record2.output = getToolErrorOutput(error2);
|
|
199903
200039
|
throw error2;
|
|
199904
200040
|
} finally {
|
|
199905
200041
|
record2.ms = context.recordedMs ?? Date.now() - startedAt;
|
|
200042
|
+
if (journalCallId && this.rootInvocationId && this.options.onJournalEvent && !this.options.signal?.aborted) {
|
|
200043
|
+
this.options.onJournalEvent({
|
|
200044
|
+
type: "tool",
|
|
200045
|
+
timestamp: Date.now(),
|
|
200046
|
+
rootInvocationId: this.rootInvocationId,
|
|
200047
|
+
invocationId: parentHop.invocationId,
|
|
200048
|
+
callId: journalCallId,
|
|
200049
|
+
agent: parentHop.agent,
|
|
200050
|
+
toolKind: definition.kind,
|
|
200051
|
+
name: definition.name,
|
|
200052
|
+
state: "completed",
|
|
200053
|
+
...toolFailed ? { failed: true } : {}
|
|
200054
|
+
});
|
|
200055
|
+
}
|
|
199906
200056
|
if (context.childInvocationId) {
|
|
199907
200057
|
record2.childInvocationId = context.childInvocationId;
|
|
199908
200058
|
}
|
|
@@ -199942,10 +200092,13 @@ var AgentInvocationRunner = class {
|
|
|
199942
200092
|
this.options.onInvocationProgress?.({ phase: "completed", hop });
|
|
199943
200093
|
return hop;
|
|
199944
200094
|
}
|
|
199945
|
-
async invokeAgent(agent, input2, depth, parentInvocationId, ancestors, conversation, hopPath) {
|
|
200095
|
+
async invokeAgent(agent, input2, depth, parentInvocationId, ancestors, conversation, hopPath, parentAgentName) {
|
|
199946
200096
|
throwIfSequenceCancelled(this.options.signal);
|
|
199947
200097
|
const startedAt = Date.now();
|
|
199948
200098
|
const invocationId = this.nextInvocationId();
|
|
200099
|
+
if (depth === 0) {
|
|
200100
|
+
this.rootInvocationId = invocationId;
|
|
200101
|
+
}
|
|
199949
200102
|
const hop = {
|
|
199950
200103
|
invocationId,
|
|
199951
200104
|
...parentInvocationId ? { parentInvocationId } : {},
|
|
@@ -199971,6 +200124,18 @@ var AgentInvocationRunner = class {
|
|
|
199971
200124
|
depth,
|
|
199972
200125
|
cycle: hop.cycle
|
|
199973
200126
|
});
|
|
200127
|
+
if (parentInvocationId && parentAgentName && this.rootInvocationId && this.options.onJournalEvent && !this.options.signal?.aborted) {
|
|
200128
|
+
this.options.onJournalEvent({
|
|
200129
|
+
type: "handoff",
|
|
200130
|
+
timestamp: Date.now(),
|
|
200131
|
+
rootInvocationId: this.rootInvocationId,
|
|
200132
|
+
invocationId,
|
|
200133
|
+
parentInvocationId,
|
|
200134
|
+
parentAgent: parentAgentName,
|
|
200135
|
+
agent: agent.name,
|
|
200136
|
+
input: input2
|
|
200137
|
+
});
|
|
200138
|
+
}
|
|
199974
200139
|
this.emitActivity(hop, "active", "preparing");
|
|
199975
200140
|
let resolvedModel;
|
|
199976
200141
|
let fatalToolError;
|
|
@@ -200031,6 +200196,9 @@ var AgentInvocationRunner = class {
|
|
|
200031
200196
|
invocationId
|
|
200032
200197
|
);
|
|
200033
200198
|
let systemPrompt = preparedKnowledge?.assembly.systemPrompt ?? authoredSystemPrompt;
|
|
200199
|
+
if (this.options.onJournalEvent && !requestOverride) {
|
|
200200
|
+
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.";
|
|
200201
|
+
}
|
|
200034
200202
|
hop.accepts = this.validateContract(agent, "accepts", input2);
|
|
200035
200203
|
if (hop.accepts && !hop.accepts.assertion.passed) {
|
|
200036
200204
|
throw new Error(contractFailureMessage(hop.accepts));
|
|
@@ -200205,6 +200373,16 @@ ${TOOL_RESULT_RECOVERY_INSTRUCTIONS}`;
|
|
|
200205
200373
|
if (reportedError instanceof AgentProviderRefusalError) {
|
|
200206
200374
|
hop.output.text = reportedError.rawText;
|
|
200207
200375
|
}
|
|
200376
|
+
if (hop.error && this.rootInvocationId && this.options.onJournalEvent) {
|
|
200377
|
+
this.options.onJournalEvent({
|
|
200378
|
+
type: "runtime",
|
|
200379
|
+
timestamp: Date.now(),
|
|
200380
|
+
rootInvocationId: this.rootInvocationId,
|
|
200381
|
+
invocationId,
|
|
200382
|
+
agent: agent.name,
|
|
200383
|
+
summary: `${agent.name} stopped: ${hop.error.slice(0, 280)}`
|
|
200384
|
+
});
|
|
200385
|
+
}
|
|
200208
200386
|
} finally {
|
|
200209
200387
|
await this.options.mcpSessionManager?.closeScope(invocationId);
|
|
200210
200388
|
if (this.options.signal?.aborted) {
|
|
@@ -200255,9 +200433,13 @@ ${TOOL_RESULT_RECOVERY_INSTRUCTIONS}`;
|
|
|
200255
200433
|
overrideKeys.add(key);
|
|
200256
200434
|
}
|
|
200257
200435
|
let callerLimitBreaches = 0;
|
|
200258
|
-
|
|
200436
|
+
const reservedAgentNames = ["load_knowledge", "norn_read_tool_result", "norn_search_tool_results"];
|
|
200437
|
+
if (this.options.onJournalEvent || requestToolsOverride?.some((tool) => tool.kind === "journal")) {
|
|
200438
|
+
reservedAgentNames.push("report_progress");
|
|
200439
|
+
}
|
|
200440
|
+
if (agent.callableAgents.some((reference) => reservedAgentNames.includes(reference.name.toLowerCase()))) {
|
|
200259
200441
|
throw new Error(
|
|
200260
|
-
|
|
200442
|
+
`Agent names ${reservedAgentNames.map((name) => `'${name}'`).join(", ")} are reserved by Norn.`
|
|
200261
200443
|
);
|
|
200262
200444
|
}
|
|
200263
200445
|
if (knowledge?.hasLoadableKnowledge) {
|
|
@@ -200504,7 +200686,8 @@ ${TOOL_RESULT_RECOVERY_INSTRUCTIONS}`;
|
|
|
200504
200686
|
parentHop.invocationId,
|
|
200505
200687
|
ancestors,
|
|
200506
200688
|
[...conversation],
|
|
200507
|
-
childHopPath
|
|
200689
|
+
childHopPath,
|
|
200690
|
+
parentHop.agent
|
|
200508
200691
|
);
|
|
200509
200692
|
toolContext.childInvocationId = child.invocationId;
|
|
200510
200693
|
for (const retry2 of pendingInputRetries) {
|
|
@@ -200560,11 +200743,44 @@ ${TOOL_RESULT_RECOVERY_INSTRUCTIONS}`;
|
|
|
200560
200743
|
retry2.correctedByInvocationId = child.invocationId;
|
|
200561
200744
|
}
|
|
200562
200745
|
outputContractFailures = 0;
|
|
200563
|
-
|
|
200746
|
+
const returnedOutput = Object.prototype.hasOwnProperty.call(child.output, "body") ? child.output.body : child.output.text;
|
|
200747
|
+
if (this.rootInvocationId && this.options.onJournalEvent && !this.options.signal?.aborted) {
|
|
200748
|
+
this.options.onJournalEvent({
|
|
200749
|
+
type: "return",
|
|
200750
|
+
timestamp: Date.now(),
|
|
200751
|
+
rootInvocationId: this.rootInvocationId,
|
|
200752
|
+
invocationId: child.invocationId,
|
|
200753
|
+
parentInvocationId: parentHop.invocationId,
|
|
200754
|
+
agent: child.agent,
|
|
200755
|
+
parentAgent: parentHop.agent,
|
|
200756
|
+
output: returnedOutput
|
|
200757
|
+
});
|
|
200758
|
+
}
|
|
200759
|
+
return returnedOutput;
|
|
200564
200760
|
}));
|
|
200565
200761
|
}
|
|
200762
|
+
const offeredJournal = requestToolsOverride?.find((tool) => tool.kind === "journal" && tool.name === "report_progress");
|
|
200763
|
+
if (this.options.onJournalEvent || offeredJournal) {
|
|
200764
|
+
providerTools.push(this.createRecordedProviderTool(parentHop, {
|
|
200765
|
+
name: "report_progress",
|
|
200766
|
+
providerName: offeredJournal?.providerName ?? "norn_report_progress",
|
|
200767
|
+
kind: "journal",
|
|
200768
|
+
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.",
|
|
200769
|
+
inputSchema: offeredJournal?.inputSchema ?? {
|
|
200770
|
+
type: "object",
|
|
200771
|
+
properties: {
|
|
200772
|
+
kind: { type: "string", enum: ["new_work", "finding", "obstacle", "direction", "milestone"] },
|
|
200773
|
+
summary: { type: "string", minLength: 1, maxLength: 280 },
|
|
200774
|
+
next: { type: "string", maxLength: 160 }
|
|
200775
|
+
},
|
|
200776
|
+
required: ["kind", "summary"],
|
|
200777
|
+
additionalProperties: false
|
|
200778
|
+
}
|
|
200779
|
+
}, async (input2) => this.reportJournalStatus(parentHop, input2)));
|
|
200780
|
+
}
|
|
200781
|
+
const hasRecoverableTools = providerTools.some((tool) => tool.kind !== "journal");
|
|
200566
200782
|
const offeredContext = requestToolsOverride?.find((tool) => tool.name === "norn_read_tool_result" && tool.kind === "context");
|
|
200567
|
-
if (!requestToolsOverride &&
|
|
200783
|
+
if (!requestToolsOverride && hasRecoverableTools || offeredContext) {
|
|
200568
200784
|
providerTools.push(this.createRecordedProviderTool(parentHop, {
|
|
200569
200785
|
name: "norn_read_tool_result",
|
|
200570
200786
|
providerName: offeredContext?.providerName ?? "norn_read_tool_result",
|
|
@@ -200583,7 +200799,7 @@ ${TOOL_RESULT_RECOVERY_INSTRUCTIONS}`;
|
|
|
200583
200799
|
}, async (input2) => this.toolResultStore.read(input2, parentHop.invocationId)));
|
|
200584
200800
|
}
|
|
200585
200801
|
const offeredSearch = requestToolsOverride?.find((tool) => tool.name === "norn_search_tool_results" && tool.kind === "context");
|
|
200586
|
-
if (!requestToolsOverride &&
|
|
200802
|
+
if (!requestToolsOverride && hasRecoverableTools || offeredSearch) {
|
|
200587
200803
|
providerTools.push(this.createRecordedProviderTool(parentHop, {
|
|
200588
200804
|
name: "norn_search_tool_results",
|
|
200589
200805
|
providerName: offeredSearch?.providerName ?? "norn_search_tool_results",
|
|
@@ -201927,7 +202143,7 @@ function isSafeProjectRelativePath(value) {
|
|
|
201927
202143
|
return isRelativePath(value) && !normalizePathSeparators(value).split("/").includes("..");
|
|
201928
202144
|
}
|
|
201929
202145
|
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");
|
|
202146
|
+
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
202147
|
}
|
|
201932
202148
|
function isAgentKnowledgeTopicSnapshot(value) {
|
|
201933
202149
|
return isObjectRecord2(value) && typeof value.topicHash === "string" && (value.heading === void 0 || typeof value.heading === "string") && isFiniteNonNegativeNumber(value.estimatedTokens) && isStringArray(value.itemPaths);
|
|
@@ -201943,7 +202159,7 @@ function isAgentRequestSnapshot(value) {
|
|
|
201943
202159
|
(message) => isObjectRecord2(message) && (message.role === "user" || message.role === "assistant") && typeof message.content === "string"
|
|
201944
202160
|
);
|
|
201945
202161
|
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)
|
|
202162
|
+
(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
202163
|
);
|
|
201948
202164
|
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
202165
|
String(value.knowledge.routingRule)
|
|
@@ -207581,7 +207797,19 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
|
|
|
207581
207797
|
nestingDepth,
|
|
207582
207798
|
agentActivity
|
|
207583
207799
|
});
|
|
207584
|
-
}
|
|
207800
|
+
},
|
|
207801
|
+
...executionContext?.agentWorkJournal ? {
|
|
207802
|
+
onJournalEvent: (agentJournal) => {
|
|
207803
|
+
onProgress?.({
|
|
207804
|
+
currentStep: stepIdx + 1,
|
|
207805
|
+
totalSteps,
|
|
207806
|
+
stepType: "agent",
|
|
207807
|
+
stepDescription: `${agentJournal.agent} work journal`,
|
|
207808
|
+
nestingDepth,
|
|
207809
|
+
agentJournal
|
|
207810
|
+
});
|
|
207811
|
+
}
|
|
207812
|
+
} : {}
|
|
207585
207813
|
});
|
|
207586
207814
|
const run = await runner.run(agent, input2);
|
|
207587
207815
|
hop = run.hop;
|
|
@@ -208052,7 +208280,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
|
|
|
208052
208280
|
let requestText = `${parsed.method} ${parsed.url}`;
|
|
208053
208281
|
const contentLines = step.content.split("\n");
|
|
208054
208282
|
if (contentLines.length > 1) {
|
|
208055
|
-
const extraLines = contentLines.slice(1)
|
|
208283
|
+
const extraLines = contentLines.slice(1);
|
|
208056
208284
|
const hasExplicitBlank = extraLines.some((line2) => line2.trim() === "");
|
|
208057
208285
|
const firstNonEmpty = extraLines.find((line2) => line2.trim() !== "");
|
|
208058
208286
|
const startsWithHeader = firstNonEmpty ? /^[A-Za-z0-9\-_]+\s*:/.test(firstNonEmpty.trim()) : false;
|
|
@@ -208295,6 +208523,7 @@ ${indentMultiline(userMessage)}`;
|
|
|
208295
208523
|
agentEventLog,
|
|
208296
208524
|
agentRootInvocationCounts,
|
|
208297
208525
|
recordAgentRuns: executionContext?.recordAgentRuns,
|
|
208526
|
+
agentWorkJournal: executionContext?.agentWorkJournal,
|
|
208298
208527
|
agentRecordingOutputPath: executionContext?.agentRecordingOutputPath,
|
|
208299
208528
|
knowledgeCoordinator,
|
|
208300
208529
|
signal: executionContext?.signal
|
|
@@ -209528,7 +209757,7 @@ function formatAgentHopAtDepth(hop, options, displayDepth) {
|
|
|
209528
209757
|
const renderedChildren = /* @__PURE__ */ new Set();
|
|
209529
209758
|
const childrenByInvocationId = new Map(hop.children.map((child) => [child.invocationId, child]));
|
|
209530
209759
|
for (const toolCall of hop.output.toolCalls) {
|
|
209531
|
-
if (toolCall.kind === "context" || toolCall.kind === "knowledge" && isKnowledgeAlreadyLoaded(toolCall.output)) {
|
|
209760
|
+
if (toolCall.kind === "context" || toolCall.kind === "journal" || toolCall.kind === "knowledge" && isKnowledgeAlreadyLoaded(toolCall.output)) {
|
|
209532
209761
|
continue;
|
|
209533
209762
|
}
|
|
209534
209763
|
if (toolCall.kind === "agent" || toolCall.childInvocationId) {
|
|
@@ -214683,7 +214912,7 @@ async function main() {
|
|
|
214683
214912
|
}
|
|
214684
214913
|
const args = rawArgs[0] === "run" ? rawArgs.slice(1) : rawArgs;
|
|
214685
214914
|
if (args.length === 1 && (args[0] === "--version" || args[0] === "-V")) {
|
|
214686
|
-
console.log(`Norn CLI ${"4.
|
|
214915
|
+
console.log(`Norn CLI ${"4.6.1"}`);
|
|
214687
214916
|
return;
|
|
214688
214917
|
}
|
|
214689
214918
|
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.2",
|
|
6
6
|
"publisher": "Norn-PeterKrustanov",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Peter Krastanov"
|