@agent-native/core 0.204.0 → 0.204.1-nightly-20261008034100
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/action.js +5 -6
- package/dist/agent/auto-continue.d.ts +35 -0
- package/dist/agent/auto-continue.js +38 -0
- package/dist/agent/connection-required-note.d.ts +52 -0
- package/dist/agent/connection-required-note.js +187 -0
- package/dist/agent/jev-tool-prefetch.d.ts +5 -1
- package/dist/agent/jev-tool-prefetch.js +19 -6
- package/dist/agent/previous-turn-note.d.ts +9 -0
- package/dist/agent/previous-turn-note.js +48 -0
- package/dist/agent/production-agent.d.ts +17 -13
- package/dist/agent/production-agent.js +185 -43
- package/dist/agent/reaped-run-thread.d.ts +13 -0
- package/dist/agent/reaped-run-thread.js +45 -0
- package/dist/agent/run-store.d.ts +29 -5
- package/dist/agent/run-store.js +144 -30
- package/dist/agent/thread-data-builder.d.ts +16 -0
- package/dist/agent/thread-data-builder.js +30 -8
- package/dist/agent/thread-message-projection.js +5 -3
- package/dist/agent/tool-search.js +5 -1
- package/dist/agent/types.d.ts +3 -0
- package/dist/audit/config.d.ts +6 -0
- package/dist/audit/config.js +12 -1
- package/dist/audit/index.d.ts +1 -1
- package/dist/audit/index.js +1 -1
- package/dist/audit/store.js +39 -4
- package/dist/cli/skills.d.ts +1 -0
- package/dist/cli/skills.js +1 -1
- package/dist/client/chat/agentkit-agent-native.js +83 -7
- package/dist/client/chat/agentkit-protocol.js +140 -67
- package/dist/client/chat/runtime.js +5 -3
- package/dist/client/use-run-stuck-detection.d.ts +18 -1
- package/dist/client/use-run-stuck-detection.js +115 -75
- package/dist/db/request-telemetry.d.ts +7 -1
- package/dist/db/request-telemetry.js +20 -1
- package/dist/deploy/build.d.ts +25 -1
- package/dist/deploy/build.js +176 -3
- package/dist/deploy/workspace-deploy.js +8 -3
- package/dist/extensions/fetch-tool.js +50 -14
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/integrations/adapters/email.js +11 -3
- package/dist/integrations/adapters/telegram.js +4 -2
- package/dist/jobs/actions/automation-no-op.d.ts +12 -0
- package/dist/jobs/actions/automation-no-op.js +20 -0
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/jobs/background-automation-runner.d.ts +8 -1
- package/dist/jobs/background-automation-runner.js +102 -19
- package/dist/jobs/run-history.d.ts +1 -1
- package/dist/jobs/run-history.js +8 -4
- package/dist/jobs/scheduler.js +25 -12
- package/dist/localization/automation-outcome-messages.d.ts +22 -0
- package/dist/localization/automation-outcome-messages.js +90 -0
- package/dist/localization/default-messages.d.ts +2 -0
- package/dist/localization/default-messages.js +4 -2
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/metrics.d.ts +3 -0
- package/dist/observability/metrics.js +10 -0
- package/dist/observability/migrations.js +22 -0
- package/dist/observability/routes.d.ts +13 -8
- package/dist/observability/routes.js +88 -14
- package/dist/observability/sentiment.d.ts +5 -0
- package/dist/observability/sentiment.js +75 -25
- package/dist/observability/store.d.ts +28 -0
- package/dist/observability/store.js +58 -0
- package/dist/observability/traces.js +37 -5
- package/dist/org/index.d.ts +2 -2
- package/dist/progress/actions.js +1 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.js +6 -2
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/provider-api/index.d.ts +1 -0
- package/dist/provider-api/index.js +18 -0
- package/dist/provider-api/result-outcome.d.ts +1 -0
- package/dist/provider-api/result-outcome.js +20 -0
- package/dist/provider-api/staging.js +10 -9
- package/dist/server/agent-chat/prompt-resources.d.ts +8 -2
- package/dist/server/agent-chat/prompt-resources.js +30 -7
- package/dist/server/agent-chat/recurring-jobs-runtime.d.ts +19 -4
- package/dist/server/agent-chat/recurring-jobs-runtime.js +52 -16
- package/dist/server/agent-chat/sweep-trigger-auth.d.ts +18 -0
- package/dist/server/agent-chat/sweep-trigger-auth.js +36 -0
- package/dist/server/agent-chat-plugin.js +20 -13
- package/dist/server/agent-engine-default-model-route.d.ts +2 -2
- package/dist/server/auth.js +4 -4
- package/dist/server/framework-request-handler.js +19 -0
- package/dist/server/http-response-telemetry.js +4 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/request-context.d.ts +6 -0
- package/dist/shared/context-status.d.ts +21 -0
- package/dist/shared/context-status.js +54 -0
- package/dist/templates/chat/app/components/chat/ChatRouteContent.tsx +8 -1
- package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +8 -1
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +3 -1
- package/dist/triggers/actions/manage-automation.d.ts +7 -7
- package/dist/triggers/dispatcher.js +10 -7
- package/docs/content/audit-log.mdx +3 -3
- package/docs/content/cloudflare.mdx +27 -0
- package/docs/content/deployment-environment-variables.mdx +2 -0
- package/docs/content/durable-resume.mdx +3 -1
- package/docs/content/environment-variables.mdx +2 -0
- package/docs/content/recurring-jobs.mdx +29 -9
- package/docs/content/vercel.mdx +30 -0
- package/package.json +3 -3
- package/src/templates/chat/app/components/chat/ChatRouteContent.tsx +8 -1
- package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +8 -1
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +3 -1
package/dist/agent/run-store.js
CHANGED
|
@@ -6,7 +6,7 @@ import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
|
6
6
|
import { lockAndAssertServicePrincipalMayStartRun, prepareServicePrincipalRunStart, recordServicePrincipalDenial, } from "../org/service-principal-guard.js";
|
|
7
7
|
import { captureError } from "../server/capture-error.js";
|
|
8
8
|
import { isRequestedStopAbortReason } from "./abort-reasons.js";
|
|
9
|
-
import { admitAutoContinue, } from "./auto-continue.js";
|
|
9
|
+
import { admitAutoContinue, admitManualContinue, } from "./auto-continue.js";
|
|
10
10
|
import { LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, } from "./engine/credential-errors.js";
|
|
11
11
|
import { isContinuationTerminalReason } from "./types.js";
|
|
12
12
|
let _initPromise;
|
|
@@ -697,6 +697,31 @@ export async function countRunsForTurn(threadId, turnId) {
|
|
|
697
697
|
const count = Number(raw);
|
|
698
698
|
return Number.isFinite(count) ? count : 0;
|
|
699
699
|
}
|
|
700
|
+
/**
|
|
701
|
+
* The newest run of the turn before `turnId` in a thread: how the previous
|
|
702
|
+
* request ended. Null when the thread has no earlier turn.
|
|
703
|
+
*/
|
|
704
|
+
export async function getPreviousTurnNewestRun(threadId, turnId) {
|
|
705
|
+
await ensureRunTables();
|
|
706
|
+
const { rows } = await getDbExec().execute({
|
|
707
|
+
sql: `SELECT COALESCE(turn_id, id) AS turn_id, status, terminal_reason
|
|
708
|
+
FROM agent_runs
|
|
709
|
+
WHERE thread_id = ?
|
|
710
|
+
AND COALESCE(turn_id, id) <> ?
|
|
711
|
+
AND dispatch_mode IS DISTINCT FROM 'turn-abort'
|
|
712
|
+
ORDER BY started_at DESC
|
|
713
|
+
LIMIT 1`,
|
|
714
|
+
args: [threadId, turnId],
|
|
715
|
+
});
|
|
716
|
+
const row = rows[0];
|
|
717
|
+
return row
|
|
718
|
+
? {
|
|
719
|
+
turnId: row.turn_id,
|
|
720
|
+
status: row.status,
|
|
721
|
+
terminalReason: row.terminal_reason,
|
|
722
|
+
}
|
|
723
|
+
: null;
|
|
724
|
+
}
|
|
700
725
|
export async function getTurnInitiatorByRun(runId) {
|
|
701
726
|
await ensureRunTables();
|
|
702
727
|
const { rows } = await getDbExec().execute({
|
|
@@ -810,7 +835,12 @@ export async function tryClaimRunSlot(threadId, runId, maxStaleMs, options) {
|
|
|
810
835
|
const client = getDbExec();
|
|
811
836
|
const now = Date.now();
|
|
812
837
|
const turnId = options?.turnId ?? runId;
|
|
813
|
-
const
|
|
838
|
+
const continueOf = options?.continueOf;
|
|
839
|
+
// A continuation names a run that already ended, so replaying the turn's
|
|
840
|
+
// last finished run would hand back the stop it asked to move past.
|
|
841
|
+
const replayCompletedTurn = options?.replayCompletedTurn === true &&
|
|
842
|
+
Boolean(options.turnId) &&
|
|
843
|
+
!continueOf;
|
|
814
844
|
if (!client.transaction) {
|
|
815
845
|
throw new Error("Atomic run-slot claims require transaction support");
|
|
816
846
|
}
|
|
@@ -885,40 +915,54 @@ export async function tryClaimRunSlot(threadId, runId, maxStaleMs, options) {
|
|
|
885
915
|
}
|
|
886
916
|
// Admitted under the thread's slot lock, so two tabs continuing the same
|
|
887
917
|
// stop start one run and the count of continuations stays exact.
|
|
888
|
-
if (
|
|
918
|
+
if (continueOf) {
|
|
889
919
|
const turnRuns = await tx.execute({
|
|
890
920
|
sql: `SELECT id, status, terminal_reason, completed_at,
|
|
891
921
|
COUNT(auto_continue_of) OVER () AS auto_continues,
|
|
892
|
-
MIN(started_at) OVER () AS turn_started_at
|
|
922
|
+
MIN(started_at) OVER () AS turn_started_at,
|
|
923
|
+
EXISTS (
|
|
924
|
+
SELECT 1 FROM agent_runs later
|
|
925
|
+
WHERE later.thread_id = ?
|
|
926
|
+
AND COALESCE(later.turn_id, later.id) <> ?
|
|
927
|
+
AND later.started_at > agent_runs.started_at
|
|
928
|
+
) AS later_turn_started
|
|
893
929
|
FROM agent_runs
|
|
894
930
|
WHERE thread_id = ? AND turn_id = ?
|
|
895
931
|
AND dispatch_mode IS DISTINCT FROM 'turn-abort'
|
|
896
932
|
ORDER BY started_at DESC LIMIT 1`,
|
|
897
|
-
args: [threadId, turnId],
|
|
933
|
+
args: [threadId, turnId, threadId, turnId],
|
|
898
934
|
});
|
|
899
935
|
const row = turnRuns.rows[0];
|
|
900
|
-
const
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
936
|
+
const turn = row
|
|
937
|
+
? {
|
|
938
|
+
newest: {
|
|
939
|
+
id: row.id,
|
|
940
|
+
status: row.status,
|
|
941
|
+
terminalReason: row.terminal_reason,
|
|
942
|
+
completedAt: row.completed_at === null ? null : Number(row.completed_at),
|
|
943
|
+
},
|
|
944
|
+
autoContinues: Number(row.auto_continues),
|
|
945
|
+
startedAt: Number(row.turn_started_at),
|
|
946
|
+
}
|
|
947
|
+
: null;
|
|
948
|
+
const admission = continueOf.trigger === "auto"
|
|
949
|
+
? admitAutoContinue({
|
|
950
|
+
turn,
|
|
951
|
+
stoppedRunId: continueOf.runId,
|
|
952
|
+
nowMs: now,
|
|
953
|
+
})
|
|
954
|
+
: admitManualContinue({
|
|
955
|
+
turn,
|
|
956
|
+
stoppedRunId: continueOf.runId,
|
|
957
|
+
laterTurnStarted: row?.later_turn_started === true ||
|
|
958
|
+
row?.later_turn_started === "t",
|
|
959
|
+
});
|
|
916
960
|
if (!admission.admit) {
|
|
917
961
|
return {
|
|
918
962
|
result: {
|
|
919
963
|
claimed: false,
|
|
920
964
|
activeRunId: null,
|
|
921
|
-
|
|
965
|
+
continueRefused: admission.code,
|
|
922
966
|
},
|
|
923
967
|
};
|
|
924
968
|
}
|
|
@@ -937,7 +981,8 @@ export async function tryClaimRunSlot(threadId, runId, maxStaleMs, options) {
|
|
|
937
981
|
options?.dispatchMode ?? null,
|
|
938
982
|
options?.dispatchPayload ?? null,
|
|
939
983
|
continuationOrder,
|
|
940
|
-
|
|
984
|
+
// Only automatic continuations count toward the turn's cap.
|
|
985
|
+
continueOf?.trigger === "auto" ? continueOf.runId : null,
|
|
941
986
|
],
|
|
942
987
|
});
|
|
943
988
|
if ((inserted.rowsAffected ?? 0) !== 1) {
|
|
@@ -1416,7 +1461,7 @@ function priorDiagStageLabel(raw) {
|
|
|
1416
1461
|
}
|
|
1417
1462
|
return raw.slice(0, 120);
|
|
1418
1463
|
}
|
|
1419
|
-
async function reapSingleStaleRun(runId, maxStaleMs) {
|
|
1464
|
+
async function reapSingleStaleRun(runId, source, maxStaleMs) {
|
|
1420
1465
|
const completedAt = Date.now();
|
|
1421
1466
|
const staleClause = typeof maxStaleMs === "number"
|
|
1422
1467
|
? `${livenessBasisSql()} < ?`
|
|
@@ -1511,6 +1556,11 @@ async function reapSingleStaleRun(runId, maxStaleMs) {
|
|
|
1511
1556
|
forensics = read.forensics;
|
|
1512
1557
|
priorStageInfo = read.priorStageInfo;
|
|
1513
1558
|
}
|
|
1559
|
+
// Saved before the successor starts: a successor that folds its own reply
|
|
1560
|
+
// first would have this older run's steps land after its, and take its
|
|
1561
|
+
// error and status.
|
|
1562
|
+
if (reaped)
|
|
1563
|
+
await finalizeStaleRun(runId, source);
|
|
1514
1564
|
if (reaped && outcome && outcome.outcome !== "not_background") {
|
|
1515
1565
|
const outcomeDetail = outcome.outcome === "recovered"
|
|
1516
1566
|
? `recovered successorRunId=${outcome.successorRunId}`
|
|
@@ -1532,12 +1582,9 @@ export async function reapIfStale(runId, maxStaleMs) {
|
|
|
1532
1582
|
await ensureRunTables();
|
|
1533
1583
|
if (await reconcileTerminalRunFromEvents(runId))
|
|
1534
1584
|
return false;
|
|
1535
|
-
const reaped = await reapSingleStaleRun(runId, maxStaleMs);
|
|
1585
|
+
const reaped = await reapSingleStaleRun(runId, "reap-if-stale", maxStaleMs);
|
|
1536
1586
|
if (!reaped && (await reconcileTerminalRunFromEvents(runId)))
|
|
1537
1587
|
return false;
|
|
1538
|
-
if (reaped) {
|
|
1539
|
-
await safeAppendTerminalRunEvent(runId, STALE_RUN_ERROR_EVENT, "reap-if-stale");
|
|
1540
|
-
}
|
|
1541
1588
|
return reaped;
|
|
1542
1589
|
}
|
|
1543
1590
|
export async function reapUnclaimedBackgroundRun(runId) {
|
|
@@ -1826,6 +1873,51 @@ export async function getRunById(runId) {
|
|
|
1826
1873
|
terminalReason: r.terminal_reason ?? null,
|
|
1827
1874
|
};
|
|
1828
1875
|
}
|
|
1876
|
+
/**
|
|
1877
|
+
* What saving a run the server ended on its behalf needs: the run's ledger,
|
|
1878
|
+
* and whether anything ran after it in its turn or in a later turn.
|
|
1879
|
+
*/
|
|
1880
|
+
export async function readStoppedRunForThreadFold(runId) {
|
|
1881
|
+
await ensureRunTables();
|
|
1882
|
+
const client = getDbExec();
|
|
1883
|
+
const { rows } = await client.execute({
|
|
1884
|
+
sql: `SELECT r.thread_id, COALESCE(r.turn_id, r.id) AS turn_id,
|
|
1885
|
+
r.status, r.started_at,
|
|
1886
|
+
EXISTS (
|
|
1887
|
+
SELECT 1 FROM agent_runs later
|
|
1888
|
+
WHERE later.thread_id = r.thread_id
|
|
1889
|
+
AND COALESCE(later.turn_id, later.id) = COALESCE(r.turn_id, r.id)
|
|
1890
|
+
AND later.id <> r.id
|
|
1891
|
+
AND later.started_at >= r.started_at
|
|
1892
|
+
AND later.dispatch_mode IS DISTINCT FROM 'turn-abort'
|
|
1893
|
+
) AS continued_in_turn,
|
|
1894
|
+
EXISTS (
|
|
1895
|
+
SELECT 1 FROM agent_runs later
|
|
1896
|
+
WHERE later.thread_id = r.thread_id
|
|
1897
|
+
AND COALESCE(later.turn_id, later.id) <> COALESCE(r.turn_id, r.id)
|
|
1898
|
+
AND later.started_at > r.started_at
|
|
1899
|
+
) AS later_turn_started
|
|
1900
|
+
FROM agent_runs r WHERE r.id = ?`,
|
|
1901
|
+
args: [runId],
|
|
1902
|
+
});
|
|
1903
|
+
const row = rows[0];
|
|
1904
|
+
// A sweep can name a run whose worker answered again before it was reaped;
|
|
1905
|
+
// that worker still saves its own reply.
|
|
1906
|
+
if (!row || row.status === "running")
|
|
1907
|
+
return null;
|
|
1908
|
+
const events = [];
|
|
1909
|
+
for (const { seq, eventData } of await getRunEventsSince(runId, 0)) {
|
|
1910
|
+
events.push({ seq, event: JSON.parse(eventData) });
|
|
1911
|
+
}
|
|
1912
|
+
return {
|
|
1913
|
+
threadId: row.thread_id,
|
|
1914
|
+
turnId: row.turn_id,
|
|
1915
|
+
startedAt: Number(row.started_at),
|
|
1916
|
+
events,
|
|
1917
|
+
continuedInTurn: row.continued_in_turn === true || row.continued_in_turn === "t",
|
|
1918
|
+
laterTurnStarted: row.later_turn_started === true || row.later_turn_started === "t",
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1829
1921
|
export async function getLastTerminalRunEvent(runId) {
|
|
1830
1922
|
await ensureRunTables();
|
|
1831
1923
|
const client = getDbExec();
|
|
@@ -2057,7 +2149,7 @@ export async function reapAllStaleRuns() {
|
|
|
2057
2149
|
if (typeof id !== "string")
|
|
2058
2150
|
continue;
|
|
2059
2151
|
try {
|
|
2060
|
-
if (await reapSingleStaleRun(id))
|
|
2152
|
+
if (await reapSingleStaleRun(id, "reap-all-stale"))
|
|
2061
2153
|
reapedCount += 1;
|
|
2062
2154
|
}
|
|
2063
2155
|
catch (error) {
|
|
@@ -2256,7 +2348,7 @@ async function cleanupOldRunsInternal(olderThanMs, erroredOlderThanMs) {
|
|
|
2256
2348
|
for (const row of stale.rows) {
|
|
2257
2349
|
const id = row.id;
|
|
2258
2350
|
if (typeof id === "string") {
|
|
2259
|
-
await
|
|
2351
|
+
await finalizeStaleRun(id, "cleanup-old-runs");
|
|
2260
2352
|
}
|
|
2261
2353
|
}
|
|
2262
2354
|
await pruneAndRollUpPrunedRunOutcomes(client, cutoff, erroredCutoff);
|
|
@@ -2308,6 +2400,28 @@ export async function listErroredRuns(options) {
|
|
|
2308
2400
|
export async function ensureTerminalRunEvent(runId, event) {
|
|
2309
2401
|
return appendTerminalRunEvent(runId, event);
|
|
2310
2402
|
}
|
|
2403
|
+
/**
|
|
2404
|
+
* Ends a run the server reaped because its worker stopped: records the stop
|
|
2405
|
+
* in the run's ledger, then saves what the run did into its thread, which the
|
|
2406
|
+
* dead worker never got to do.
|
|
2407
|
+
*/
|
|
2408
|
+
async function finalizeStaleRun(runId, source) {
|
|
2409
|
+
await safeAppendTerminalRunEvent(runId, STALE_RUN_ERROR_EVENT, source);
|
|
2410
|
+
try {
|
|
2411
|
+
const { foldReapedRunIntoThread } = await import("./reaped-run-thread.js");
|
|
2412
|
+
await foldReapedRunIntoThread(runId);
|
|
2413
|
+
}
|
|
2414
|
+
catch (error) {
|
|
2415
|
+
captureError(error, {
|
|
2416
|
+
tags: {
|
|
2417
|
+
component: "agent-run-store",
|
|
2418
|
+
operation: "fold-reaped-run",
|
|
2419
|
+
source,
|
|
2420
|
+
},
|
|
2421
|
+
extra: { runId },
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2311
2425
|
async function safeAppendTerminalRunEvent(runId, event, source) {
|
|
2312
2426
|
let firstError;
|
|
2313
2427
|
try {
|
|
@@ -75,6 +75,12 @@ export declare function resumeThreadHistoryForRequest(threadData: string | Recor
|
|
|
75
75
|
messages: EngineMessage[];
|
|
76
76
|
foundTurnPrompt: boolean;
|
|
77
77
|
};
|
|
78
|
+
/**
|
|
79
|
+
* The turn the thread's newest prompt was sent in, when the server stamped
|
|
80
|
+
* it. A prompt whose run was refused before it started has no run row, so
|
|
81
|
+
* this is how a continuation learns a newer prompt is waiting.
|
|
82
|
+
*/
|
|
83
|
+
export declare function latestPromptTurnId(threadData: string | Record<string, unknown>): string | undefined;
|
|
78
84
|
export declare function threadMessageTextForEngine(message: any): string;
|
|
79
85
|
export interface CodeAgentThreadTranscriptEvent {
|
|
80
86
|
id: string;
|
|
@@ -119,6 +125,16 @@ export declare function applySubmittedUserMessage(repo: any, userMessage: UserMe
|
|
|
119
125
|
} | {
|
|
120
126
|
status: "already_claimed" | "claim_expired";
|
|
121
127
|
};
|
|
128
|
+
/**
|
|
129
|
+
* Whether a stored user message is the prompt this run answers, rather than
|
|
130
|
+
* one sent after it started. Messages saved without the server's turn stamp
|
|
131
|
+
* fall back to their creation time.
|
|
132
|
+
*/
|
|
133
|
+
export declare function isRunPrompt(user: any, run: {
|
|
134
|
+
runId: string;
|
|
135
|
+
turnId?: string | null;
|
|
136
|
+
startedAt: number;
|
|
137
|
+
}): boolean;
|
|
122
138
|
export type ThreadSuggestionRun = Pick<ActiveRun, "runId" | "threadId" | "turnId" | "startedAt" | "status" | "events" | "abortReason">;
|
|
123
139
|
export declare function foldThreadRunSuggestions(repo: any, run: ThreadSuggestionRun): any;
|
|
124
140
|
export declare function mergeThreadDataForClientSave(existingRepo: any, incomingRepo: any, options?: MergeThreadDataOptions): any;
|
|
@@ -864,6 +864,16 @@ export function resumeThreadHistoryForRequest(threadData) {
|
|
|
864
864
|
foundTurnPrompt: true,
|
|
865
865
|
};
|
|
866
866
|
}
|
|
867
|
+
/**
|
|
868
|
+
* The turn the thread's newest prompt was sent in, when the server stamped
|
|
869
|
+
* it. A prompt whose run was refused before it started has no run row, so
|
|
870
|
+
* this is how a continuation learns a newer prompt is waiting.
|
|
871
|
+
*/
|
|
872
|
+
export function latestPromptTurnId(threadData) {
|
|
873
|
+
const data = typeof threadData === "string" ? JSON.parse(threadData) : threadData;
|
|
874
|
+
const turnId = latestStoredUser(data)?.metadata?.custom?.submittedTurnId;
|
|
875
|
+
return typeof turnId === "string" ? turnId : undefined;
|
|
876
|
+
}
|
|
867
877
|
const MAX_INTEGRATION_ARTIFACTS_IN_CONTEXT = 12;
|
|
868
878
|
const MAX_INTEGRATION_ARTIFACT_FIELD_CHARS = 500;
|
|
869
879
|
function boundedString(value) {
|
|
@@ -1389,21 +1399,28 @@ function latestStoredUser(repo) {
|
|
|
1389
1399
|
?.map((entry) => getStoredMessage(entry))
|
|
1390
1400
|
.findLast((message) => message?.role === "user");
|
|
1391
1401
|
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Whether a stored user message is the prompt this run answers, rather than
|
|
1404
|
+
* one sent after it started. Messages saved without the server's turn stamp
|
|
1405
|
+
* fall back to their creation time.
|
|
1406
|
+
*/
|
|
1407
|
+
export function isRunPrompt(user, run) {
|
|
1408
|
+
const userContext = user?.metadata?.custom;
|
|
1409
|
+
if (userContext?.submittedTurnId) {
|
|
1410
|
+
return userContext.submittedTurnId === run.turnId;
|
|
1411
|
+
}
|
|
1412
|
+
return (userContext?.submittedRunId === run.runId ||
|
|
1413
|
+
!user?.createdAt ||
|
|
1414
|
+
new Date(user.createdAt).getTime() <= run.startedAt);
|
|
1415
|
+
}
|
|
1392
1416
|
function clearThreadSuggestions(repo) {
|
|
1393
1417
|
return repo.agentKit
|
|
1394
1418
|
? { ...repo, agentKit: { ...repo.agentKit, suggestions: [] } }
|
|
1395
1419
|
: repo;
|
|
1396
1420
|
}
|
|
1397
1421
|
export function foldThreadRunSuggestions(repo, run) {
|
|
1398
|
-
|
|
1399
|
-
const userContext = user?.metadata?.custom;
|
|
1400
|
-
if (userContext?.submittedTurnId
|
|
1401
|
-
? userContext.submittedTurnId !== run.turnId
|
|
1402
|
-
: userContext?.submittedRunId !== run.runId &&
|
|
1403
|
-
user?.createdAt &&
|
|
1404
|
-
new Date(user.createdAt).getTime() > run.startedAt) {
|
|
1422
|
+
if (!isRunPrompt(latestStoredUser(repo), run))
|
|
1405
1423
|
return repo;
|
|
1406
|
-
}
|
|
1407
1424
|
const previous = repo.agentKit ?? {};
|
|
1408
1425
|
const latest = latestSnapshotRun(previous.runs);
|
|
1409
1426
|
const startedAt = new Date(run.startedAt).toISOString();
|
|
@@ -2872,6 +2889,11 @@ export function foldAssistantTurn(repo, assistantMsg, options) {
|
|
|
2872
2889
|
}
|
|
2873
2890
|
if (incomingCustom.continued !== true)
|
|
2874
2891
|
delete mergedCustom.continued;
|
|
2892
|
+
// A turn's failure is its newest run's: a run that continued past an
|
|
2893
|
+
// earlier stop clears the stop's error instead of inheriting it.
|
|
2894
|
+
if (!runAlreadyFolded && incomingCustom.runError === undefined) {
|
|
2895
|
+
delete mergedCustom.runError;
|
|
2896
|
+
}
|
|
2875
2897
|
const mergedMessage = {
|
|
2876
2898
|
...lastMsg,
|
|
2877
2899
|
content: normalizeAssistantToolCallIds({
|
|
@@ -157,13 +157,15 @@ function representedToolCallMessageIds(value, toolCalls) {
|
|
|
157
157
|
? rootCall.id
|
|
158
158
|
: undefined;
|
|
159
159
|
const call = id ? byId.get(id) : undefined;
|
|
160
|
+
// A tool call's identity is its id, name, and args. Never compare results:
|
|
161
|
+
// the stored root keeps the model-facing text while the snapshot output is
|
|
162
|
+
// the structured chat-UI result, so one call never compares equal and its
|
|
163
|
+
// reply renders twice.
|
|
160
164
|
if (!call ||
|
|
161
165
|
(typeof rootCall.toolName === "string" &&
|
|
162
166
|
rootCall.toolName !== call.name) ||
|
|
163
167
|
(Object.prototype.hasOwnProperty.call(rootCall, "args") &&
|
|
164
|
-
!sameValue(rootCall.args, call.input))
|
|
165
|
-
(Object.prototype.hasOwnProperty.call(rootCall, "result") &&
|
|
166
|
-
!sameValue(rootCall.result, call.output))) {
|
|
168
|
+
!sameValue(rootCall.args, call.input))) {
|
|
167
169
|
return undefined;
|
|
168
170
|
}
|
|
169
171
|
if (typeof call.messageId === "string")
|
|
@@ -500,7 +500,11 @@ function scoreTool(input) {
|
|
|
500
500
|
.join(" "));
|
|
501
501
|
const all = `${name} ${source} ${description} ${params} ${input.kind}`;
|
|
502
502
|
const phrase = searchableText(input.query);
|
|
503
|
-
|
|
503
|
+
// A query that is a tool's whole name is a request for that tool, not for
|
|
504
|
+
// whatever else repeats its words.
|
|
505
|
+
let score = input.name.toLowerCase() === input.query.trim().toLowerCase()
|
|
506
|
+
? EXACT_NAME_SCORE
|
|
507
|
+
: 0;
|
|
504
508
|
if (name.includes(phrase))
|
|
505
509
|
score += 14;
|
|
506
510
|
if (source && source.includes(phrase))
|
package/dist/agent/types.d.ts
CHANGED
|
@@ -170,6 +170,8 @@ export interface AgentChatRequest {
|
|
|
170
170
|
internalContinuation?: boolean;
|
|
171
171
|
/** The time-limit stop this request continues, in the same turn. */
|
|
172
172
|
autoContinueOfRunId?: string;
|
|
173
|
+
/** The stopped run a person chose to continue, in the same turn. */
|
|
174
|
+
continueOfRunId?: string;
|
|
173
175
|
__backgroundRun?: {
|
|
174
176
|
runId: string;
|
|
175
177
|
turnId?: string;
|
|
@@ -273,6 +275,7 @@ export type AgentChatEvent = {
|
|
|
273
275
|
input?: AgentToolInput;
|
|
274
276
|
result: string;
|
|
275
277
|
isError?: boolean;
|
|
278
|
+
errorCode?: string;
|
|
276
279
|
completedSideEffect?: boolean;
|
|
277
280
|
replayed?: true;
|
|
278
281
|
fileMutation?: AgentFileMutationProof;
|
package/dist/audit/config.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ import type { ActionAuditConfig, AuditActorKind, AuditStatus } from "./types.js"
|
|
|
2
2
|
export declare function normalizeAuditConfig(raw: unknown): ActionAuditConfig | undefined;
|
|
3
3
|
export declare function resolveAuditAttach(config: ActionAuditConfig | undefined, readOnly: boolean | undefined): boolean;
|
|
4
4
|
export declare function shouldRecordAudit(config: ActionAuditConfig | undefined, actionName: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Agent protocols: the app's own agent (`tool`) and the MCP, WebMCP, and A2A
|
|
7
|
+
* surfaces outside agents use. A call over one records as the agent acting
|
|
8
|
+
* for the signed-in user; that user's email does not make it a click.
|
|
9
|
+
*/
|
|
10
|
+
export declare const AGENT_AUDIT_CALLERS: ReadonlySet<string>;
|
|
5
11
|
export declare function deriveActorKind(caller: string | undefined, actorEmail: string | undefined | null, orgId?: string | null): AuditActorKind;
|
|
6
12
|
export declare function isAuditDisabled(): boolean;
|
|
7
13
|
export type { AuditStatus };
|
package/dist/audit/config.js
CHANGED
|
@@ -29,11 +29,22 @@ export function shouldRecordAudit(config, actionName) {
|
|
|
29
29
|
return false;
|
|
30
30
|
return true;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Agent protocols: the app's own agent (`tool`) and the MCP, WebMCP, and A2A
|
|
34
|
+
* surfaces outside agents use. A call over one records as the agent acting
|
|
35
|
+
* for the signed-in user; that user's email does not make it a click.
|
|
36
|
+
*/
|
|
37
|
+
export const AGENT_AUDIT_CALLERS = new Set([
|
|
38
|
+
"tool",
|
|
39
|
+
"mcp",
|
|
40
|
+
"webmcp",
|
|
41
|
+
"a2a",
|
|
42
|
+
]);
|
|
32
43
|
export function deriveActorKind(caller, actorEmail, orgId) {
|
|
33
44
|
const serviceIdentity = parseServiceIdentityEmail(actorEmail);
|
|
34
45
|
if (serviceIdentity && serviceIdentity.orgId === orgId)
|
|
35
46
|
return "service";
|
|
36
|
-
if (caller
|
|
47
|
+
if (caller && AGENT_AUDIT_CALLERS.has(caller))
|
|
37
48
|
return "agent";
|
|
38
49
|
return actorEmail ? "human" : "system";
|
|
39
50
|
}
|
package/dist/audit/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { ActionAuditConfig, AuditActorKind, AuditCallMeta, AuditEvent, AuditQueryFilters, AuditStatus, AuditTarget, AuditVisibility, } from "./types.js";
|
|
2
|
-
export { deriveActorKind, isAuditDisabled, normalizeAuditConfig, resolveAuditAttach, shouldRecordAudit, } from "./config.js";
|
|
2
|
+
export { AGENT_AUDIT_CALLERS, deriveActorKind, isAuditDisabled, normalizeAuditConfig, resolveAuditAttach, shouldRecordAudit, } from "./config.js";
|
|
3
3
|
export { redactArgsToJson } from "./redact.js";
|
|
4
4
|
export { ensureAuditTables, insertAuditEvent, queryAuditEvents, queryAuditEventPage, queryAuditApps, getAuditEventById, deleteOldAuditEvents, type AuditEventPage, type AuditReadScope, type AuditTrail, } from "./store.js";
|
|
5
5
|
export { AuditAccessError, resolveAuditReadScope } from "./read-scope.js";
|
package/dist/audit/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { deriveActorKind, isAuditDisabled, normalizeAuditConfig, resolveAuditAttach, shouldRecordAudit, } from "./config.js";
|
|
1
|
+
export { AGENT_AUDIT_CALLERS, deriveActorKind, isAuditDisabled, normalizeAuditConfig, resolveAuditAttach, shouldRecordAudit, } from "./config.js";
|
|
2
2
|
export { redactArgsToJson } from "./redact.js";
|
|
3
3
|
export { ensureAuditTables, insertAuditEvent, queryAuditEvents, queryAuditEventPage, queryAuditApps, getAuditEventById, deleteOldAuditEvents, } from "./store.js";
|
|
4
4
|
export { AuditAccessError, resolveAuditReadScope } from "./read-scope.js";
|
package/dist/audit/store.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getDbExec } from "../db/client.js";
|
|
2
2
|
import { ensureColumnExists, ensureTableExists, ensureIndexExists, } from "../db/ddl-guard.js";
|
|
3
|
+
import { AGENT_AUDIT_CALLERS } from "./config.js";
|
|
3
4
|
let _initPromise;
|
|
4
5
|
export const AGENT_AUDIT_LOG_CREATE_SQL = `
|
|
5
6
|
CREATE TABLE IF NOT EXISTS agent_audit_log (
|
|
@@ -115,13 +116,44 @@ export async function insertAuditEvent(event) {
|
|
|
115
116
|
],
|
|
116
117
|
});
|
|
117
118
|
}
|
|
119
|
+
// Rows recorded before outside agents (MCP, WebMCP, A2A) counted as agents
|
|
120
|
+
// stored `human` or `system` for them. Reads classify those by caller here and
|
|
121
|
+
// in `actorKindClause`, so an old row and a filter on it agree without
|
|
122
|
+
// rewriting the append-only log. A `service` row keeps its kind: org service
|
|
123
|
+
// principals mostly call over MCP.
|
|
124
|
+
const LEGACY_AGENT_KINDS = ["human", "system"];
|
|
125
|
+
function readActorKind(storedKind, caller) {
|
|
126
|
+
return LEGACY_AGENT_KINDS.includes(storedKind) &&
|
|
127
|
+
AGENT_AUDIT_CALLERS.has(caller)
|
|
128
|
+
? "agent"
|
|
129
|
+
: storedKind;
|
|
130
|
+
}
|
|
131
|
+
function actorKindClause(kind) {
|
|
132
|
+
const callers = [...AGENT_AUDIT_CALLERS];
|
|
133
|
+
const agentCaller = `caller IN (${callers.map(() => "?").join(", ")})`;
|
|
134
|
+
if (kind === "agent") {
|
|
135
|
+
const legacy = LEGACY_AGENT_KINDS.map(() => "?").join(", ");
|
|
136
|
+
return {
|
|
137
|
+
sql: `(actor_kind = ? OR (actor_kind IN (${legacy}) AND ${agentCaller}))`,
|
|
138
|
+
args: [kind, ...LEGACY_AGENT_KINDS, ...callers],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (LEGACY_AGENT_KINDS.includes(kind)) {
|
|
142
|
+
return {
|
|
143
|
+
sql: `(actor_kind = ? AND NOT ${agentCaller})`,
|
|
144
|
+
args: [kind, ...callers],
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return { sql: "actor_kind = ?", args: [kind] };
|
|
148
|
+
}
|
|
118
149
|
function mapRow(row) {
|
|
150
|
+
const caller = String(row.caller);
|
|
119
151
|
return {
|
|
120
152
|
id: String(row.id),
|
|
121
153
|
createdAt: Number(row.created_at),
|
|
122
154
|
action: String(row.action),
|
|
123
|
-
caller
|
|
124
|
-
actorKind: row.actor_kind,
|
|
155
|
+
caller,
|
|
156
|
+
actorKind: readActorKind(row.actor_kind, caller),
|
|
125
157
|
actorEmail: row.actor_email ?? null,
|
|
126
158
|
orgId: row.org_id ?? null,
|
|
127
159
|
threadId: row.thread_id ?? null,
|
|
@@ -230,8 +262,11 @@ async function selectAuditRows(scope, filters, limit) {
|
|
|
230
262
|
push("target_type = ?", filters.targetType);
|
|
231
263
|
if (filters.targetId)
|
|
232
264
|
push("target_id = ?", filters.targetId);
|
|
233
|
-
if (filters.actorKind)
|
|
234
|
-
|
|
265
|
+
if (filters.actorKind) {
|
|
266
|
+
const clause = actorKindClause(filters.actorKind);
|
|
267
|
+
where.push(clause.sql);
|
|
268
|
+
args.push(...clause.args);
|
|
269
|
+
}
|
|
235
270
|
if (filters.actorEmail)
|
|
236
271
|
push("actor_email = ?", filters.actorEmail);
|
|
237
272
|
if (filters.status)
|
package/dist/cli/skills.d.ts
CHANGED
|
@@ -197,6 +197,7 @@ interface SkillsScopePromptContext {
|
|
|
197
197
|
interface SkillsPlanModePromptContext {
|
|
198
198
|
initialMode: PlanInstallMode;
|
|
199
199
|
}
|
|
200
|
+
export declare function builtInCommandsRootForAgent(agent: string, scope: "project" | "user", baseDir: string): string;
|
|
200
201
|
export declare function parseSkillsArgs(argv: string[]): ParsedSkillsArgs;
|
|
201
202
|
export declare function addAgentNativeSkill(parsed: ParsedSkillsArgs, options?: RunSkillsOptions): Promise<SkillsAddResult>;
|
|
202
203
|
export declare function runSkills(argv: string[], options?: RunSkillsOptions): Promise<void>;
|
package/dist/cli/skills.js
CHANGED
|
@@ -984,7 +984,7 @@ function builtInSkillsRootForAgent(agent, scope, baseDir) {
|
|
|
984
984
|
}
|
|
985
985
|
return path.join(home, ".claude", "skills");
|
|
986
986
|
}
|
|
987
|
-
function builtInCommandsRootForAgent(agent, scope, baseDir) {
|
|
987
|
+
export function builtInCommandsRootForAgent(agent, scope, baseDir) {
|
|
988
988
|
const home = homeDir() ?? baseDir;
|
|
989
989
|
if (scope === "project") {
|
|
990
990
|
if (agent === "codex")
|