@agent-native/core 0.220.0 → 0.220.1-beta-embedded-chat-20261025-20261025150325
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.d.ts +18 -0
- package/dist/action.js +15 -0
- package/dist/agent/actions/list-model-providers.d.ts +6 -0
- package/dist/agent/actions/list-model-providers.js +104 -21
- package/dist/agent/harness/background.js +119 -30
- package/dist/agent/harness/index.d.ts +1 -1
- package/dist/agent/harness/index.js +1 -1
- package/dist/agent/harness/lifecycle.d.ts +16 -0
- package/dist/agent/harness/lifecycle.js +1175 -87
- package/dist/agent/harness/migrations.js +21 -0
- package/dist/agent/harness/runner.d.ts +5 -0
- package/dist/agent/harness/runner.js +186 -43
- package/dist/agent/harness/store.d.ts +131 -0
- package/dist/agent/harness/store.js +1384 -13
- package/dist/agent/production-agent.js +76 -17
- package/dist/agent/run-loop-with-resume.js +62 -19
- package/dist/agent/run-manager.js +41 -10
- package/dist/agent/run-store.d.ts +2 -0
- package/dist/agent/run-store.fencing.worker.js +115 -3
- package/dist/agent/run-store.js +80 -30
- package/dist/app-config/observability.d.ts +5 -0
- package/dist/app-config/observability.js +7 -0
- package/dist/app-config/schema.d.ts +5 -0
- package/dist/audit/redact.d.ts +7 -1
- package/dist/audit/redact.js +62 -22
- package/dist/automation/index.d.ts +1 -1
- package/dist/cli/code-agent-executor.js +28 -5
- package/dist/cli/code-agent-mcp-config.d.ts +4 -0
- package/dist/cli/code-agent-mcp-config.js +7 -1
- package/dist/client/sign-out.js +11 -4
- package/dist/client/sse-event-processor.d.ts +7 -0
- package/dist/client/sse-event-processor.js +3 -2
- package/dist/client/use-action.d.ts +4 -1
- package/dist/client/use-action.js +3 -1
- package/dist/client/use-session.d.ts +2 -0
- package/dist/client/use-session.js +10 -0
- package/dist/code-agents/background-run.d.ts +2 -1
- package/dist/collab/agent-presence.js +9 -3
- package/dist/collab/awareness-post.d.ts +2 -0
- package/dist/collab/awareness-post.js +101 -0
- package/dist/collab/awareness-sequence.d.ts +3 -0
- package/dist/collab/awareness-sequence.js +3 -0
- package/dist/collab/awareness-store.d.ts +12 -3
- package/dist/collab/awareness-store.js +426 -60
- package/dist/collab/awareness.d.ts +23 -2
- package/dist/collab/awareness.js +376 -41
- package/dist/collab/client.js +101 -36
- package/dist/collab/payload.d.ts +4 -0
- package/dist/collab/payload.js +15 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/routes.js +4 -26
- package/dist/collab/struct-routes.js +3 -22
- package/dist/credentials/index.js +34 -15
- package/dist/db/schema-snapshot.generated.js +54 -3
- package/dist/file-upload/actions/get-file-storage.js +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/file-upload/storage-settings.d.ts +8 -1
- package/dist/file-upload/storage-settings.js +92 -21
- package/dist/identity/rekey.js +9 -0
- package/dist/integrations/actions/confirm-messaging-channel-credential-removal.d.ts +10 -0
- package/dist/integrations/actions/confirm-messaging-channel-credential-removal.js +23 -0
- package/dist/integrations/actions/list-messaging-channels.js +1 -1
- package/dist/integrations/actions/manage-messaging-channel.d.ts +4 -0
- package/dist/integrations/actions/manage-messaging-channel.js +13 -3
- package/dist/integrations/actions/prepare-messaging-channel-credential-removal.d.ts +7 -0
- package/dist/integrations/actions/prepare-messaging-channel-credential-removal.js +17 -0
- package/dist/integrations/channel-settings.d.ts +31 -4
- package/dist/integrations/channel-settings.js +353 -65
- package/dist/integrations/google-docs-poller.d.ts +9 -1
- package/dist/integrations/google-docs-poller.js +16 -5
- package/dist/integrations/pending-tasks-retry-job.d.ts +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +6 -2
- package/dist/integrations/plugin.js +36 -5
- package/dist/integrations/remote-push-delivery-job.d.ts +1 -1
- package/dist/integrations/remote-push-delivery-job.js +6 -2
- package/dist/integrations/remote-retry-job.d.ts +1 -1
- package/dist/integrations/remote-retry-job.js +6 -2
- package/dist/integrations/webhook-handler.js +4 -2
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/mcp-client/config.d.ts +2 -0
- package/dist/mcp-client/manager.js +15 -0
- package/dist/mcp-client/remote-store.d.ts +9 -0
- package/dist/mcp-client/remote-store.js +42 -11
- package/dist/mcp-client/routes.js +57 -4
- package/dist/observability/hosted-model-experiment.d.ts +63 -0
- package/dist/observability/hosted-model-experiment.js +92 -0
- package/dist/observability/traces.js +10 -9
- package/dist/org/handlers.d.ts +2 -2
- package/dist/org/index.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/provider-api/corpus-jobs.js +3 -21
- package/dist/provider-api/index.js +17 -23
- package/dist/provider-api/request-body.d.ts +1 -0
- package/dist/provider-api/request-body.js +50 -0
- package/dist/provider-api/staging.js +2 -20
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/errors.d.ts +11 -0
- package/dist/secrets/errors.js +15 -0
- package/dist/secrets/routes.d.ts +10 -10
- package/dist/secrets/storage.d.ts +27 -0
- package/dist/secrets/storage.js +141 -32
- package/dist/server/agent-chat-plugin.js +37 -0
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
- package/dist/server/background-jobs.d.ts +3 -0
- package/dist/server/background-jobs.js +33 -0
- package/dist/server/collab-plugin.js +19 -10
- package/dist/server/core-routes-plugin.d.ts +19 -1
- package/dist/server/core-routes-plugin.js +129 -17
- package/dist/server/credential-provider.d.ts +5 -4
- package/dist/server/credential-provider.js +153 -23
- package/dist/server/dev-database-lifecycle.js +13 -3
- package/dist/server/interval-job.d.ts +2 -1
- package/dist/server/interval-job.js +1 -1
- package/dist/server/secret-key-aliases.js +20 -2
- package/dist/shared/poll-engine.d.ts +2 -1
- package/dist/shared/poll-engine.js +25 -1
- package/dist/shared/progress-events.d.ts +1 -0
- package/dist/shared/progress-events.js +13 -0
- package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +95 -76
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +91 -36
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +72 -31
- package/dist/triggers/actions/manage-automation.d.ts +4 -4
- package/dist/triggers/dispatcher.d.ts +1 -0
- package/dist/triggers/dispatcher.js +28 -2
- package/package.json +4 -4
- package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +95 -76
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +91 -36
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +72 -31
- package/tsconfig.base.json +2 -0
package/dist/action.d.ts
CHANGED
|
@@ -23,8 +23,26 @@ export interface ActionAutomationContext {
|
|
|
23
23
|
triggerName: string;
|
|
24
24
|
policyId?: string;
|
|
25
25
|
}
|
|
26
|
+
export declare const DEFAULT_TOOL_RESULT_CHARS = 50000;
|
|
27
|
+
/**
|
|
28
|
+
* The most characters of a pretty-printed result the agent keeps for a call:
|
|
29
|
+
* the action's own limit, else the agent-wide limit, else the default, bounded
|
|
30
|
+
* by any hard limit. The agent loop and actions that fit their own results
|
|
31
|
+
* resolve it here so they cannot disagree.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveToolMaxResultChars(action: {
|
|
34
|
+
maxResultChars?: number;
|
|
35
|
+
}, limits?: {
|
|
36
|
+
maxResultChars?: number;
|
|
37
|
+
hardMaxResultChars?: number;
|
|
38
|
+
}): number;
|
|
26
39
|
export interface ActionRunContext {
|
|
27
40
|
send?: (event: AgentChatEvent) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Result cap the agent loop enforces on this call, from
|
|
43
|
+
* `resolveToolMaxResultChars`. Absent outside the agent loop.
|
|
44
|
+
*/
|
|
45
|
+
maxResultChars?: number;
|
|
28
46
|
userEmail?: string;
|
|
29
47
|
/**
|
|
30
48
|
* Request headers for server-only actions that must delegate to an
|
package/dist/action.js
CHANGED
|
@@ -48,6 +48,21 @@ export async function recordInvalidActionCall(entry, args, ctx, error) {
|
|
|
48
48
|
}
|
|
49
49
|
return true;
|
|
50
50
|
}
|
|
51
|
+
export const DEFAULT_TOOL_RESULT_CHARS = 50_000;
|
|
52
|
+
/**
|
|
53
|
+
* The most characters of a pretty-printed result the agent keeps for a call:
|
|
54
|
+
* the action's own limit, else the agent-wide limit, else the default, bounded
|
|
55
|
+
* by any hard limit. The agent loop and actions that fit their own results
|
|
56
|
+
* resolve it here so they cannot disagree.
|
|
57
|
+
*/
|
|
58
|
+
export function resolveToolMaxResultChars(action, limits) {
|
|
59
|
+
const configured = action.maxResultChars ??
|
|
60
|
+
limits?.maxResultChars ??
|
|
61
|
+
DEFAULT_TOOL_RESULT_CHARS;
|
|
62
|
+
return typeof limits?.hardMaxResultChars === "number"
|
|
63
|
+
? Math.min(configured, limits.hardMaxResultChars)
|
|
64
|
+
: configured;
|
|
65
|
+
}
|
|
51
66
|
export class ActionContractError extends Error {
|
|
52
67
|
actionContractError = true;
|
|
53
68
|
errorCode;
|
|
@@ -18,6 +18,12 @@ export interface ModelProviderKey {
|
|
|
18
18
|
rejectedAt?: number;
|
|
19
19
|
/** The rejection markers couldn't be read, so nobody knows if it works. */
|
|
20
20
|
rejectionUnknown?: true;
|
|
21
|
+
/** A saved key or endpoint exists but could not be decrypted. */
|
|
22
|
+
unreadable?: true;
|
|
23
|
+
/** At least one stored API key name could not be decrypted. */
|
|
24
|
+
unreadableKey?: true;
|
|
25
|
+
/** The stored OpenAI-compatible endpoint could not be decrypted. */
|
|
26
|
+
unreadableEndpoint?: true;
|
|
21
27
|
/**
|
|
22
28
|
* Stored in a legacy `workspace` row: the organization's (`workspace` /
|
|
23
29
|
* orgId) for `scope: "org"`, the pre-organization solo row
|
|
@@ -2,8 +2,9 @@ import { z } from "zod";
|
|
|
2
2
|
import { defineAction, fail } from "../../action.js";
|
|
3
3
|
import { AGENT_PROVIDER_CATALOG, } from "../../client/agent-provider-catalog.js";
|
|
4
4
|
import { canManageOrg } from "../../org/permissions.js";
|
|
5
|
+
import { AppSecretDecryptionError } from "../../secrets/errors.js";
|
|
5
6
|
import { secretKeyNames } from "../../secrets/key-aliases.js";
|
|
6
|
-
import { readAppSecrets, } from "../../secrets/storage.js";
|
|
7
|
+
import { readAppSecret, readAppSecrets, } from "../../secrets/storage.js";
|
|
7
8
|
import { readProviderCredentialRejections } from "../../server/credential-provider.js";
|
|
8
9
|
import { isPersonalProviderKeyUseRestricted, readOrgMemberRole, } from "../../server/personal-provider-key-policy.js";
|
|
9
10
|
import { getRequestContext } from "../../server/request-context.js";
|
|
@@ -30,6 +31,40 @@ function firstPresent(rows, names) {
|
|
|
30
31
|
}
|
|
31
32
|
return null;
|
|
32
33
|
}
|
|
34
|
+
async function readScopeSecrets(source, keys) {
|
|
35
|
+
try {
|
|
36
|
+
return {
|
|
37
|
+
rows: await readAppSecrets({ keys, ...source }),
|
|
38
|
+
unreadableKeys: new Set(),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (!(error instanceof AppSecretDecryptionError) ||
|
|
43
|
+
error.scope !== source.scope ||
|
|
44
|
+
error.scopeId !== source.scopeId) {
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const rows = new Map();
|
|
49
|
+
const unreadableKeys = new Set();
|
|
50
|
+
await Promise.all(keys.map(async (key) => {
|
|
51
|
+
try {
|
|
52
|
+
const row = await readAppSecret({ key, ...source });
|
|
53
|
+
if (row)
|
|
54
|
+
rows.set(key, row);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error instanceof AppSecretDecryptionError &&
|
|
58
|
+
error.scope === source.scope &&
|
|
59
|
+
error.scopeId === source.scopeId) {
|
|
60
|
+
unreadableKeys.add(key);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}));
|
|
66
|
+
return { rows, unreadableKeys };
|
|
67
|
+
}
|
|
33
68
|
/**
|
|
34
69
|
* The stored rows the credential resolver reads for one listing scope, in its
|
|
35
70
|
* precedence order. Legacy `workspace` rows power chats like the first-class
|
|
@@ -50,35 +85,70 @@ async function readScopeKeys(scope, scopeId, reveal) {
|
|
|
50
85
|
const names = providerKeyNames();
|
|
51
86
|
const keys = names.flatMap(({ primary, endpoint }) => endpoint ? [...primary, endpoint] : primary);
|
|
52
87
|
const sources = storedRowsFor(scope, scopeId);
|
|
53
|
-
const reads = await Promise.all(sources.map((source) =>
|
|
88
|
+
const reads = await Promise.all(sources.map((source) => readScopeSecrets(source, keys)));
|
|
54
89
|
const found = [];
|
|
55
90
|
const endpointProviders = new Set();
|
|
56
91
|
for (const entry of names) {
|
|
57
|
-
|
|
58
|
-
|
|
92
|
+
let unreadableEndpointIndex = null;
|
|
93
|
+
let resolved = false;
|
|
94
|
+
for (const [index, read] of reads.entries()) {
|
|
95
|
+
const { rows, unreadableKeys } = read;
|
|
96
|
+
const endpoint = entry.endpoint
|
|
97
|
+
? firstPresent(rows, [entry.endpoint])
|
|
98
|
+
: null;
|
|
99
|
+
const unreadablePrimary = entry.primary.some((key) => unreadableKeys.has(key));
|
|
100
|
+
const unreadableEndpoint = !!entry.endpoint && unreadableKeys.has(entry.endpoint);
|
|
101
|
+
if (unreadableEndpoint && unreadableEndpointIndex === null) {
|
|
102
|
+
unreadableEndpointIndex = index;
|
|
103
|
+
}
|
|
104
|
+
if (endpoint || unreadableEndpoint) {
|
|
59
105
|
endpointProviders.add(entry.option.id);
|
|
60
106
|
}
|
|
61
107
|
const row = firstPresent(rows, entry.primary);
|
|
62
|
-
if (
|
|
63
|
-
|
|
108
|
+
if (row) {
|
|
109
|
+
found.push({
|
|
110
|
+
names: entry,
|
|
111
|
+
row,
|
|
112
|
+
endpoint,
|
|
113
|
+
legacy: sources[index].scope === "workspace",
|
|
114
|
+
unreadableKey: unreadablePrimary,
|
|
115
|
+
unreadableEndpoint: unreadableEndpoint || unreadableEndpointIndex !== null,
|
|
116
|
+
});
|
|
117
|
+
resolved = true;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
if (unreadablePrimary) {
|
|
121
|
+
found.push({
|
|
122
|
+
names: entry,
|
|
123
|
+
row: null,
|
|
124
|
+
endpoint: null,
|
|
125
|
+
legacy: sources[index].scope === "workspace",
|
|
126
|
+
unreadableKey: true,
|
|
127
|
+
unreadableEndpoint: unreadableEndpointIndex !== null,
|
|
128
|
+
});
|
|
129
|
+
resolved = true;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!resolved && unreadableEndpointIndex !== null) {
|
|
64
134
|
found.push({
|
|
65
135
|
names: entry,
|
|
66
|
-
row,
|
|
67
|
-
endpoint:
|
|
68
|
-
legacy: sources[
|
|
136
|
+
row: null,
|
|
137
|
+
endpoint: null,
|
|
138
|
+
legacy: sources[unreadableEndpointIndex].scope === "workspace",
|
|
139
|
+
unreadableKey: false,
|
|
140
|
+
unreadableEndpoint: true,
|
|
69
141
|
});
|
|
70
|
-
break;
|
|
71
142
|
}
|
|
72
143
|
}
|
|
73
144
|
// Markers are fingerprinted with the canonical key name, the one the
|
|
74
145
|
// runtime records a rejection under, whichever alias the value is stored as.
|
|
75
|
-
const checkable = found.
|
|
146
|
+
const checkable = found.flatMap(({ names, row }) => names.option.id !== "ollama" && row
|
|
147
|
+
? [{ key: names.option.key, value: row.value }]
|
|
148
|
+
: []);
|
|
76
149
|
let rejections;
|
|
77
150
|
try {
|
|
78
|
-
rejections = await readProviderCredentialRejections(checkable
|
|
79
|
-
key: names.option.key,
|
|
80
|
-
value: row.value,
|
|
81
|
-
})));
|
|
151
|
+
rejections = await readProviderCredentialRejections(checkable);
|
|
82
152
|
}
|
|
83
153
|
catch (error) {
|
|
84
154
|
console.warn("[list-model-providers] could not read rejection markers", {
|
|
@@ -87,28 +157,41 @@ async function readScopeKeys(scope, scopeId, reveal) {
|
|
|
87
157
|
rejections = null;
|
|
88
158
|
}
|
|
89
159
|
const result = new Map();
|
|
90
|
-
for (const { names: entry, row, endpoint, legacy } of found) {
|
|
160
|
+
for (const { names: entry, row, endpoint, legacy, unreadableKey, unreadableEndpoint, } of found) {
|
|
91
161
|
const id = entry.option.id;
|
|
92
162
|
const isOllama = id === "ollama";
|
|
93
|
-
const rejection = !isOllama &&
|
|
163
|
+
const rejection = !isOllama && row && rejections
|
|
164
|
+
? rejections.get(entry.option.key)
|
|
165
|
+
: undefined;
|
|
94
166
|
result.set(id, {
|
|
95
167
|
scope,
|
|
96
|
-
...(reveal
|
|
168
|
+
...(row && reveal
|
|
97
169
|
? {
|
|
98
170
|
...(isOllama ? { endpoint: row.value } : { masked: row.last4 }),
|
|
99
171
|
...(endpoint?.value ? { endpoint: endpoint.value } : {}),
|
|
100
172
|
}
|
|
101
173
|
: {}),
|
|
102
|
-
updatedAt: row
|
|
174
|
+
updatedAt: row?.updatedAt || null,
|
|
175
|
+
...(unreadableKey || unreadableEndpoint
|
|
176
|
+
? {
|
|
177
|
+
unreadable: true,
|
|
178
|
+
...(unreadableKey ? { unreadableKey: true } : {}),
|
|
179
|
+
...(unreadableEndpoint
|
|
180
|
+
? { unreadableEndpoint: true }
|
|
181
|
+
: {}),
|
|
182
|
+
}
|
|
183
|
+
: {}),
|
|
103
184
|
...(rejection ? { rejectedAt: rejection.at } : {}),
|
|
104
|
-
...(!isOllama && !rejections
|
|
185
|
+
...(!isOllama && row && !rejections
|
|
186
|
+
? { rejectionUnknown: true }
|
|
187
|
+
: {}),
|
|
105
188
|
...(legacy ? { legacyWorkspaceRow: true } : {}),
|
|
106
189
|
});
|
|
107
190
|
}
|
|
108
191
|
return { keys: result, endpointProviders };
|
|
109
192
|
}
|
|
110
193
|
export default defineAction({
|
|
111
|
-
description: "List the model providers on Settings › Model: for each provider (OpenRouter, Ollama, Anthropic, OpenAI, Google Gemini, Groq, Mistral, Cohere), the organization's key and the caller's personal key, masked, with the endpoint URL saved for Ollama or an OpenAI-compatible gateway and when the provider rejected the key (rejectedAt: chats that use it stop until someone replaces it). Members see only that an organization key exists, not its mask. A key in a legacy workspace row (the organization's, or the caller's pre-organization solo row) lists at the scope chats resolve it as, marked legacyWorkspaceRow; removing the provider at that scope deletes it. Also returns whether the caller can manage organization providers, whether personal API keys are restricted for them (their personal keys are then stored but unused), and the stored default model. Builder.io connections come from the Builder.io status, not here. Never returns a key value. Related: get-provider-models and manage-provider-models (which models each provider shows), manage-agent-engine (the default model), manage-provider-key-policy (Restrict personal API keys), preview-secret-removal (what removing a key stops).",
|
|
194
|
+
description: "List the model providers on Settings › Model: for each provider (OpenRouter, Ollama, Anthropic, OpenAI, Google Gemini, Groq, Mistral, Cohere), the organization's key and the caller's personal key, masked, with the endpoint URL saved for Ollama or an OpenAI-compatible gateway and when the provider rejected the key (rejectedAt: chats that use it stop until someone replaces it). A key entry marked unreadable means saved provider data could not be decrypted; unreadableKey and unreadableEndpoint identify which value. Owners and admins can replace or clear the unreadable value from the row. Members see only that an organization key exists, not its mask. A key in a legacy workspace row (the organization's, or the caller's pre-organization solo row) lists at the scope chats resolve it as, marked legacyWorkspaceRow; removing the provider at that scope deletes it. Also returns whether the caller can manage organization providers, whether personal API keys are restricted for them (their personal keys are then stored but unused), and the stored default model. Builder.io connections come from the Builder.io status, not here. Never returns a key value. Related: get-provider-models and manage-provider-models (which models each provider shows), manage-agent-engine (the default model), manage-provider-key-policy (Restrict personal API keys), preview-secret-removal (what removing a key stops).",
|
|
112
195
|
schema: z.object({}),
|
|
113
196
|
http: { method: "GET" },
|
|
114
197
|
readOnly: true,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { redactArgsToValue, redactTextToSummary } from "../../audit/redact.js";
|
|
2
2
|
import { abortRunDurably } from "../run-manager.js";
|
|
3
3
|
import { getRunEventsSince } from "../run-store.js";
|
|
4
|
-
import { resolveAgentHarnessApproval, sendAgentHarnessFollowUp,
|
|
5
|
-
import { getAgentHarnessSessionByRunId,
|
|
4
|
+
import { resolveAgentHarnessApproval, sendAgentHarnessFollowUp, stopAndPersistAgentHarnessSession, } from "./lifecycle.js";
|
|
5
|
+
import { getAgentHarnessSessionByRunId, listAgentHarnessSessionEvents, listAgentHarnessSessions, } from "./store.js";
|
|
6
6
|
export function createAgentHarnessBackgroundAgentController(scope = { ownerEmail: null }) {
|
|
7
7
|
return {
|
|
8
8
|
list: (options) => listAgentHarnessBackgroundRuns({
|
|
@@ -47,11 +47,35 @@ export async function listAgentHarnessBackgroundTranscriptEvents(runId, options
|
|
|
47
47
|
if (!session?.runId)
|
|
48
48
|
return [];
|
|
49
49
|
const events = await getRunEventsSince(session.runId, 0);
|
|
50
|
-
|
|
50
|
+
const runTranscript = events
|
|
51
51
|
.map((event) => parseRunEvent(event))
|
|
52
52
|
.filter((event) => Boolean(event))
|
|
53
|
-
.map((
|
|
54
|
-
|
|
53
|
+
.map(({ runEvent, eventAt, transcriptOrder }) => ({
|
|
54
|
+
transcriptOrder,
|
|
55
|
+
sourceRank: 0,
|
|
56
|
+
sourceSeq: runEvent.seq,
|
|
57
|
+
event: toAgentHarnessBackgroundTranscriptEvent(session, runEvent, eventAt),
|
|
58
|
+
}))
|
|
59
|
+
.filter((entry) => Boolean(entry.event));
|
|
60
|
+
const sessionTranscript = (await listAgentHarnessSessionEvents(session.id))
|
|
61
|
+
.map((event) => ({
|
|
62
|
+
transcriptOrder: event.transcriptOrder,
|
|
63
|
+
sourceRank: 1,
|
|
64
|
+
sourceSeq: event.seq,
|
|
65
|
+
event: toAgentHarnessBackgroundSessionTranscriptEvent(session, event),
|
|
66
|
+
}))
|
|
67
|
+
.filter((entry) => Boolean(entry.event));
|
|
68
|
+
return assignCanonicalTranscriptSequence([...runTranscript, ...sessionTranscript]
|
|
69
|
+
.sort(compareTranscriptOrder)
|
|
70
|
+
.map(({ event, transcriptOrder }) => transcriptOrder === null
|
|
71
|
+
? {
|
|
72
|
+
...event,
|
|
73
|
+
metadata: {
|
|
74
|
+
...(event.metadata ?? {}),
|
|
75
|
+
transcriptOrderFallback: "legacy_source_order",
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
: event));
|
|
55
79
|
}
|
|
56
80
|
export async function stopAgentHarnessBackgroundRun(runId, options = {
|
|
57
81
|
ownerEmail: null,
|
|
@@ -64,6 +88,7 @@ export async function stopAgentHarnessBackgroundRun(runId, options = {
|
|
|
64
88
|
return missingHarnessRunResult(runId);
|
|
65
89
|
}
|
|
66
90
|
const failures = [];
|
|
91
|
+
let stoppedSession = session;
|
|
67
92
|
if (session.runId) {
|
|
68
93
|
try {
|
|
69
94
|
await abortRunDurably(session.runId, "user", { allowNotStarted: true });
|
|
@@ -73,16 +98,16 @@ export async function stopAgentHarnessBackgroundRun(runId, options = {
|
|
|
73
98
|
}
|
|
74
99
|
}
|
|
75
100
|
try {
|
|
76
|
-
await
|
|
101
|
+
const stopped = await stopAndPersistAgentHarnessSession({
|
|
77
102
|
sessionId: session.id,
|
|
78
103
|
scope: options,
|
|
104
|
+
status: failures.length > 0 ? "errored" : "stopped",
|
|
79
105
|
});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
await markAgentHarnessSessionStopped(session.id, failures.length > 0 ? "errored" : "stopped");
|
|
106
|
+
stoppedSession = stopped ?? {
|
|
107
|
+
...session,
|
|
108
|
+
status: "stopped",
|
|
109
|
+
stoppedAt: Date.now(),
|
|
110
|
+
};
|
|
86
111
|
}
|
|
87
112
|
catch (error) {
|
|
88
113
|
failures.push(error);
|
|
@@ -95,11 +120,7 @@ export async function stopAgentHarnessBackgroundRun(runId, options = {
|
|
|
95
120
|
return {
|
|
96
121
|
ok: true,
|
|
97
122
|
runId,
|
|
98
|
-
run: toAgentHarnessBackgroundRun(
|
|
99
|
-
...session,
|
|
100
|
-
status: "stopped",
|
|
101
|
-
stoppedAt: Date.now(),
|
|
102
|
-
}),
|
|
123
|
+
run: toAgentHarnessBackgroundRun(stoppedSession),
|
|
103
124
|
};
|
|
104
125
|
}
|
|
105
126
|
function toAgentHarnessBackgroundRun(session) {
|
|
@@ -144,37 +165,84 @@ function toAgentHarnessBackgroundRun(session) {
|
|
|
144
165
|
status: session.status,
|
|
145
166
|
pendingApproval: session.pendingApproval,
|
|
146
167
|
workspaceRef: session.workspaceRef,
|
|
168
|
+
followUpOutcome: session.followUpOutcome,
|
|
147
169
|
},
|
|
148
170
|
};
|
|
149
171
|
}
|
|
150
|
-
function toAgentHarnessBackgroundTranscriptEvent(session, runEvent) {
|
|
151
|
-
|
|
172
|
+
function toAgentHarnessBackgroundTranscriptEvent(session, runEvent, eventAt) {
|
|
173
|
+
return toAgentHarnessTranscriptEvent({
|
|
174
|
+
session,
|
|
175
|
+
event: runEvent.event,
|
|
176
|
+
eventId: `${session.runId ?? session.id}:${runEvent.seq}`,
|
|
177
|
+
seq: runEvent.seq,
|
|
178
|
+
createdAt: eventAt ?? session.createdAt,
|
|
179
|
+
sourceRecordType: "agent-harness-run-event",
|
|
180
|
+
...(eventAt === null
|
|
181
|
+
? { metadata: { createdAtFallback: "session-created-at" } }
|
|
182
|
+
: {}),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function toAgentHarnessBackgroundSessionTranscriptEvent(session, sessionEvent) {
|
|
186
|
+
const runId = session.runId ?? session.id;
|
|
187
|
+
const eventId = `${runId}:operation:${sessionEvent.operationId}:${sessionEvent.seq}`;
|
|
188
|
+
return toAgentHarnessTranscriptEvent({
|
|
189
|
+
session,
|
|
190
|
+
event: sessionEvent.event,
|
|
191
|
+
eventId,
|
|
192
|
+
seq: sessionEvent.seq,
|
|
193
|
+
createdAt: sessionEvent.eventAt,
|
|
194
|
+
sourceRecordType: "agent-harness-session-event",
|
|
195
|
+
operationId: sessionEvent.operationId,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
function toAgentHarnessTranscriptEvent(input) {
|
|
199
|
+
const { session } = input;
|
|
200
|
+
const summary = summarizeAgentChatEvent(input.event);
|
|
152
201
|
if (!summary)
|
|
153
202
|
return null;
|
|
154
|
-
const
|
|
203
|
+
const runId = session.runId ?? session.id;
|
|
155
204
|
return {
|
|
156
205
|
schemaVersion: 1,
|
|
157
|
-
id: eventId,
|
|
158
|
-
runId
|
|
206
|
+
id: input.eventId,
|
|
207
|
+
runId,
|
|
159
208
|
kind: summary.kind,
|
|
160
209
|
source: "agent-harness",
|
|
161
210
|
sourceRecord: {
|
|
162
|
-
type:
|
|
163
|
-
id: eventId,
|
|
164
|
-
seq:
|
|
211
|
+
type: input.sourceRecordType,
|
|
212
|
+
id: input.eventId,
|
|
213
|
+
seq: input.seq,
|
|
214
|
+
...(input.operationId ? { operationId: input.operationId } : {}),
|
|
165
215
|
},
|
|
166
216
|
message: summary.message,
|
|
167
|
-
createdAt: new Date(
|
|
217
|
+
createdAt: new Date(input.createdAt).toISOString(),
|
|
168
218
|
metadata: {
|
|
169
219
|
...(summary.metadata ?? {}),
|
|
170
|
-
|
|
220
|
+
...(input.metadata ?? {}),
|
|
221
|
+
seq: input.seq,
|
|
171
222
|
harnessName: session.harnessName,
|
|
172
223
|
sessionId: session.id,
|
|
224
|
+
...(input.operationId ? { operationId: input.operationId } : {}),
|
|
173
225
|
},
|
|
174
226
|
};
|
|
175
227
|
}
|
|
228
|
+
function assignCanonicalTranscriptSequence(events) {
|
|
229
|
+
return events.map((event, seq) => {
|
|
230
|
+
const sourceSeq = event.sourceRecord.seq;
|
|
231
|
+
return {
|
|
232
|
+
...event,
|
|
233
|
+
sourceRecord: { ...event.sourceRecord, seq },
|
|
234
|
+
metadata: {
|
|
235
|
+
...(event.metadata ?? {}),
|
|
236
|
+
seq,
|
|
237
|
+
...(sourceSeq === undefined ? {} : { sourceSeq }),
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
});
|
|
241
|
+
}
|
|
176
242
|
function summarizeAgentChatEvent(event) {
|
|
177
243
|
switch (event.type) {
|
|
244
|
+
case "user_input":
|
|
245
|
+
return { kind: "user", message: event.text };
|
|
178
246
|
case "text":
|
|
179
247
|
return { kind: "note", message: event.text };
|
|
180
248
|
case "thinking":
|
|
@@ -233,19 +301,40 @@ function summarizeAgentChatEvent(event) {
|
|
|
233
301
|
function parseRunEvent(row) {
|
|
234
302
|
try {
|
|
235
303
|
const event = JSON.parse(row.eventData);
|
|
236
|
-
|
|
304
|
+
const eventAt = typeof row.eventAt === "number" && Number.isFinite(row.eventAt)
|
|
305
|
+
? row.eventAt
|
|
306
|
+
: null;
|
|
307
|
+
const transcriptOrder = typeof row.transcriptOrder === "number" &&
|
|
308
|
+
Number.isFinite(row.transcriptOrder)
|
|
309
|
+
? row.transcriptOrder
|
|
310
|
+
: null;
|
|
311
|
+
return {
|
|
312
|
+
runEvent: { seq: Number(row.seq), event },
|
|
313
|
+
eventAt,
|
|
314
|
+
transcriptOrder,
|
|
315
|
+
};
|
|
237
316
|
}
|
|
238
317
|
catch {
|
|
239
318
|
return null;
|
|
240
319
|
}
|
|
241
320
|
}
|
|
321
|
+
function compareTranscriptOrder(left, right) {
|
|
322
|
+
if (left.transcriptOrder != null && right.transcriptOrder != null) {
|
|
323
|
+
return left.transcriptOrder - right.transcriptOrder;
|
|
324
|
+
}
|
|
325
|
+
if (left.transcriptOrder == null && right.transcriptOrder != null)
|
|
326
|
+
return -1;
|
|
327
|
+
if (left.transcriptOrder != null && right.transcriptOrder == null)
|
|
328
|
+
return 1;
|
|
329
|
+
return left.sourceRank - right.sourceRank || left.sourceSeq - right.sourceSeq;
|
|
330
|
+
}
|
|
242
331
|
function backgroundStatus(status, needsApproval) {
|
|
332
|
+
if (needsApproval)
|
|
333
|
+
return "needs-approval";
|
|
243
334
|
if (status === "running")
|
|
244
335
|
return "running";
|
|
245
336
|
if (status === "errored")
|
|
246
337
|
return "errored";
|
|
247
|
-
if (needsApproval)
|
|
248
|
-
return "needs-approval";
|
|
249
338
|
if (status === "idle")
|
|
250
339
|
return "paused";
|
|
251
340
|
if (status === "stopped" || status === "destroyed")
|
|
@@ -3,7 +3,7 @@ export { agentHarnessEventToAgentChatEvents, stringifyResult as stringifyAgentHa
|
|
|
3
3
|
export { getAgentHarnessEntry, isAgentHarnessPackageInstalled, listAgentHarnesses, registerAgentHarness, resolveAgentHarness, type AgentHarnessEntry, } from "./registry.js";
|
|
4
4
|
export { ensureAgentHarnessSessionTables, getAgentHarnessSession, getAgentHarnessSessionByRunId, getLatestAgentHarnessSessionForThread, listAgentHarnessSessions, markAgentHarnessSessionStopped, saveAgentHarnessSession, updateAgentHarnessSession, type AgentHarnessSessionStatus, type SaveAgentHarnessSessionInput, type StoredAgentHarnessSession, } from "./store.js";
|
|
5
5
|
export { sendAgentHarnessEvent, startAgentHarnessRun, type StartAgentHarnessRunOptions, } from "./runner.js";
|
|
6
|
-
export { registerLiveAgentHarnessSession, releaseLiveAgentHarnessSession, resolveAgentHarnessApproval, sendAgentHarnessFollowUp, stopLiveAgentHarnessSession, sweepExpiredAgentHarnessSessions, type AgentHarnessLifecycleErrorCode, type AgentHarnessLifecycleResult, type AgentHarnessOwnerScope, } from "./lifecycle.js";
|
|
6
|
+
export { registerLiveAgentHarnessSession, releaseLiveAgentHarnessSession, resolveAgentHarnessApproval, sendAgentHarnessFollowUp, stopLiveAgentHarnessSession, sweepExpiredAgentHarnessApprovalResolutions, sweepExpiredAgentHarnessSessions, type AgentHarnessLifecycleErrorCode, type AgentHarnessLifecycleResult, type AgentHarnessOwnerScope, } from "./lifecycle.js";
|
|
7
7
|
export { aiSdkHarnessPartToEvents, createAiSdkHarnessAdapter, type AiSdkHarnessAdapterOptions, type AiSdkHarnessRuntime, } from "./ai-sdk-adapter.js";
|
|
8
8
|
export { ACP_PACKAGE, acpAutoPermissionDecision, acpContentBlockToText, acpFileChangeEventsFromToolContent, acpUpdateToHarnessEvents, buildAcpPromptBlocks, createAcpHarnessAdapter, resolveAcpWorkspacePath, selectAcpPermissionOption, type AcpHarnessAdapterOptions, type AcpSessionUpdate, } from "./acp-adapter.js";
|
|
9
9
|
export { BUILTIN_ACP_PRESETS, registerBuiltinAcpHarnesses, } from "./acp-builtin.js";
|
|
@@ -2,7 +2,7 @@ export { agentHarnessEventToAgentChatEvents, stringifyResult as stringifyAgentHa
|
|
|
2
2
|
export { getAgentHarnessEntry, isAgentHarnessPackageInstalled, listAgentHarnesses, registerAgentHarness, resolveAgentHarness, } from "./registry.js";
|
|
3
3
|
export { ensureAgentHarnessSessionTables, getAgentHarnessSession, getAgentHarnessSessionByRunId, getLatestAgentHarnessSessionForThread, listAgentHarnessSessions, markAgentHarnessSessionStopped, saveAgentHarnessSession, updateAgentHarnessSession, } from "./store.js";
|
|
4
4
|
export { sendAgentHarnessEvent, startAgentHarnessRun, } from "./runner.js";
|
|
5
|
-
export { registerLiveAgentHarnessSession, releaseLiveAgentHarnessSession, resolveAgentHarnessApproval, sendAgentHarnessFollowUp, stopLiveAgentHarnessSession, sweepExpiredAgentHarnessSessions, } from "./lifecycle.js";
|
|
5
|
+
export { registerLiveAgentHarnessSession, releaseLiveAgentHarnessSession, resolveAgentHarnessApproval, sendAgentHarnessFollowUp, stopLiveAgentHarnessSession, sweepExpiredAgentHarnessApprovalResolutions, sweepExpiredAgentHarnessSessions, } from "./lifecycle.js";
|
|
6
6
|
export { aiSdkHarnessPartToEvents, createAiSdkHarnessAdapter, } from "./ai-sdk-adapter.js";
|
|
7
7
|
export { ACP_PACKAGE, acpAutoPermissionDecision, acpContentBlockToText, acpFileChangeEventsFromToolContent, acpUpdateToHarnessEvents, buildAcpPromptBlocks, createAcpHarnessAdapter, resolveAcpWorkspacePath, selectAcpPermissionOption, } from "./acp-adapter.js";
|
|
8
8
|
export { BUILTIN_ACP_PRESETS, registerBuiltinAcpHarnesses, } from "./acp-builtin.js";
|
|
@@ -22,7 +22,10 @@ export declare function registerLiveAgentHarnessSession(input: {
|
|
|
22
22
|
createSession?: AgentHarnessCreateSessionOptions;
|
|
23
23
|
ownerEmail?: string | null;
|
|
24
24
|
orgId?: string | null;
|
|
25
|
+
generation: number;
|
|
25
26
|
}): void;
|
|
27
|
+
export declare function refreshLiveAgentHarnessSessionGeneration(sessionId: string, session: AgentHarnessSession, generation: number): void;
|
|
28
|
+
export declare function getLiveAgentHarnessSessionGeneration(sessionId: string, session: AgentHarnessSession): number | null;
|
|
26
29
|
export declare function releaseLiveAgentHarnessSession(sessionId: string, session?: AgentHarnessSession): void;
|
|
27
30
|
export declare function resolveAgentHarnessApproval(input: {
|
|
28
31
|
runId: string;
|
|
@@ -41,4 +44,17 @@ export declare function stopLiveAgentHarnessSession(input: {
|
|
|
41
44
|
sessionId: string;
|
|
42
45
|
scope: AgentHarnessOwnerScope;
|
|
43
46
|
}): Promise<boolean>;
|
|
47
|
+
export declare function stopAndPersistAgentHarnessSession(input: {
|
|
48
|
+
sessionId: string;
|
|
49
|
+
scope: AgentHarnessOwnerScope;
|
|
50
|
+
status: "stopped" | "errored";
|
|
51
|
+
}): Promise<StoredAgentHarnessSession | null>;
|
|
44
52
|
export declare function sweepExpiredAgentHarnessSessions(now?: number): Promise<number>;
|
|
53
|
+
export declare function sweepExpiredAgentHarnessFollowUps(limit?: number): Promise<{
|
|
54
|
+
recovered: number;
|
|
55
|
+
truncated: boolean;
|
|
56
|
+
}>;
|
|
57
|
+
export declare function sweepExpiredAgentHarnessApprovalResolutions(limit?: number): Promise<{
|
|
58
|
+
recovered: number;
|
|
59
|
+
truncated: boolean;
|
|
60
|
+
}>;
|