@agentex/agent 0.0.24 → 0.0.26
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/CHANGELOG.md +338 -0
- package/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/derived.d.ts +5 -3
- package/dist/derived.d.ts.map +1 -1
- package/dist/derived.js +11 -7
- package/dist/derived.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/acp/index.d.ts +1 -1
- package/dist/providers/acp/index.d.ts.map +1 -1
- package/dist/providers/acp/index.js +5 -97
- package/dist/providers/acp/index.js.map +1 -1
- package/dist/providers/acp/session.d.ts +8 -1
- package/dist/providers/acp/session.d.ts.map +1 -1
- package/dist/providers/acp/session.js +94 -0
- package/dist/providers/acp/session.js.map +1 -1
- package/dist/providers/claude/attach.d.ts +8 -0
- package/dist/providers/claude/attach.d.ts.map +1 -0
- package/dist/providers/claude/attach.js +113 -0
- package/dist/providers/claude/attach.js.map +1 -0
- package/dist/providers/claude/goal-capability.d.ts +15 -0
- package/dist/providers/claude/goal-capability.d.ts.map +1 -0
- package/dist/providers/claude/goal-capability.js +20 -0
- package/dist/providers/claude/goal-capability.js.map +1 -0
- package/dist/providers/claude/index.d.ts.map +1 -1
- package/dist/providers/claude/index.js +8 -4
- package/dist/providers/claude/index.js.map +1 -1
- package/dist/providers/claude/session.d.ts +11 -9
- package/dist/providers/claude/session.d.ts.map +1 -1
- package/dist/providers/claude/session.js +29 -14
- package/dist/providers/claude/session.js.map +1 -1
- package/dist/providers/codex/attach.d.ts +9 -0
- package/dist/providers/codex/attach.d.ts.map +1 -0
- package/dist/providers/codex/attach.js +93 -0
- package/dist/providers/codex/attach.js.map +1 -0
- package/dist/providers/codex/goal-capability.d.ts +13 -0
- package/dist/providers/codex/goal-capability.d.ts.map +1 -0
- package/dist/providers/codex/goal-capability.js +18 -0
- package/dist/providers/codex/goal-capability.js.map +1 -0
- package/dist/providers/codex/index.d.ts +1 -0
- package/dist/providers/codex/index.d.ts.map +1 -1
- package/dist/providers/codex/index.js +9 -6
- package/dist/providers/codex/index.js.map +1 -1
- package/dist/providers/codex/session.d.ts +11 -7
- package/dist/providers/codex/session.d.ts.map +1 -1
- package/dist/providers/codex/session.js +24 -12
- package/dist/providers/codex/session.js.map +1 -1
- package/dist/providers/codex/transcript-normalize.d.ts +28 -0
- package/dist/providers/codex/transcript-normalize.d.ts.map +1 -0
- package/dist/providers/codex/transcript-normalize.js +191 -0
- package/dist/providers/codex/transcript-normalize.js.map +1 -0
- package/dist/providers/cursor/index.d.ts.map +1 -1
- package/dist/providers/cursor/index.js +2 -2
- package/dist/providers/cursor/index.js.map +1 -1
- package/dist/providers/openclaw/index.d.ts.map +1 -1
- package/dist/providers/openclaw/index.js +2 -2
- package/dist/providers/openclaw/index.js.map +1 -1
- package/dist/providers/opencode/index.d.ts.map +1 -1
- package/dist/providers/opencode/index.js +3 -5
- package/dist/providers/opencode/index.js.map +1 -1
- package/dist/providers/pi/index.d.ts.map +1 -1
- package/dist/providers/pi/index.js +3 -5
- package/dist/providers/pi/index.js.map +1 -1
- package/dist/providers/process/index.d.ts.map +1 -1
- package/dist/providers/process/index.js +2 -2
- package/dist/providers/process/index.js.map +1 -1
- package/dist/registry.d.ts +0 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +0 -4
- package/dist/registry.js.map +1 -1
- package/dist/sessions/index.d.ts +3 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/sessions/index.js +2 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/sessions/record.d.ts +43 -0
- package/dist/sessions/record.d.ts.map +1 -0
- package/dist/sessions/record.js +85 -0
- package/dist/sessions/record.js.map +1 -0
- package/dist/types.d.ts +119 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/uuid.d.ts +7 -1
- package/dist/utils/uuid.d.ts.map +1 -1
- package/dist/utils/uuid.js +21 -1
- package/dist/utils/uuid.js.map +1 -1
- package/package.json +64 -7
- package/src/derived.ts +311 -0
- package/src/goals/controller.ts +442 -0
- package/src/goals/index.ts +21 -0
- package/src/goals/normalize.ts +173 -0
- package/src/goals/sentinel.ts +90 -0
- package/src/index.ts +270 -0
- package/src/providers/_shared/http-agent.ts +304 -0
- package/src/providers/acp/index.ts +103 -0
- package/src/providers/acp/parse.ts +131 -0
- package/src/providers/acp/session.ts +744 -0
- package/src/providers/claude/attach.ts +147 -0
- package/src/providers/claude/codec.ts +43 -0
- package/src/providers/claude/execute.ts +300 -0
- package/src/providers/claude/goal-capability.ts +21 -0
- package/src/providers/claude/index.ts +72 -0
- package/src/providers/claude/mcp.ts +82 -0
- package/src/providers/claude/parse.ts +824 -0
- package/src/providers/claude/session.ts +1192 -0
- package/src/providers/claude/transcript.ts +555 -0
- package/src/providers/codex/attach.ts +123 -0
- package/src/providers/codex/codec.ts +50 -0
- package/src/providers/codex/execute.ts +337 -0
- package/src/providers/codex/goal-capability.ts +19 -0
- package/src/providers/codex/index.ts +57 -0
- package/src/providers/codex/modes.ts +159 -0
- package/src/providers/codex/parse.ts +691 -0
- package/src/providers/codex/plan-mode.ts +49 -0
- package/src/providers/codex/session.ts +1287 -0
- package/src/providers/codex/transcript-normalize.ts +197 -0
- package/src/providers/codex/transcript.ts +487 -0
- package/src/providers/codex/usage-scanner.ts +178 -0
- package/src/providers/copilot/index.ts +19 -0
- package/src/providers/cursor/codec.ts +44 -0
- package/src/providers/cursor/execute.ts +271 -0
- package/src/providers/cursor/index.ts +25 -0
- package/src/providers/cursor/parse.ts +288 -0
- package/src/providers/gemini/index.ts +21 -0
- package/src/providers/openclaw/codec.ts +40 -0
- package/src/providers/openclaw/execute.ts +19 -0
- package/src/providers/openclaw/index.ts +29 -0
- package/src/providers/opencode/codec.ts +50 -0
- package/src/providers/opencode/event-parse.ts +141 -0
- package/src/providers/opencode/execute.ts +251 -0
- package/src/providers/opencode/http-session.ts +427 -0
- package/src/providers/opencode/index.ts +30 -0
- package/src/providers/opencode/parse.ts +203 -0
- package/src/providers/opencode/server.ts +0 -0
- package/src/providers/pi/codec.ts +44 -0
- package/src/providers/pi/execute.ts +297 -0
- package/src/providers/pi/index.ts +30 -0
- package/src/providers/pi/parse.ts +231 -0
- package/src/providers/pi/session.ts +381 -0
- package/src/providers/process/execute.ts +148 -0
- package/src/providers/process/index.ts +52 -0
- package/src/registry.ts +40 -0
- package/src/sessions/index.ts +8 -0
- package/src/sessions/record.ts +108 -0
- package/src/types.ts +1638 -0
- package/src/utils/ask-user-question.ts +57 -0
- package/src/utils/auth.ts +661 -0
- package/src/utils/binary.ts +179 -0
- package/src/utils/endpoint.ts +172 -0
- package/src/utils/env.ts +63 -0
- package/src/utils/execute-all.ts +68 -0
- package/src/utils/exit-plan-mode.ts +40 -0
- package/src/utils/instructions.ts +427 -0
- package/src/utils/process.ts +223 -0
- package/src/utils/runtime-config.ts +100 -0
- package/src/utils/runtime-homes.ts +49 -0
- package/src/utils/skill-commands.ts +493 -0
- package/src/utils/skills.ts +500 -0
- package/src/utils/template.ts +16 -0
- package/src/utils/tool-names.ts +51 -0
- package/src/utils/uuid.ts +21 -0
- package/src/utils/workspace.ts +156 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { ExecutionContext, ExecutionResult } from "../../types.js";
|
|
2
|
+
import { ensureCommandResolvable } from "../../utils/binary.js";
|
|
3
|
+
import { buildEnv, ensurePathInEnv } from "../../utils/env.js";
|
|
4
|
+
import { runChildProcess, deriveErrorCode } from "../../utils/process.js";
|
|
5
|
+
import { resolveInstructions } from "../../utils/instructions.js";
|
|
6
|
+
import { prepareWorkspace } from "../../utils/workspace.js";
|
|
7
|
+
import type { PreparedWorkspace } from "../../utils/workspace.js";
|
|
8
|
+
import { uuidv7 } from "../../utils/uuid.js";
|
|
9
|
+
|
|
10
|
+
export async function executeProcessProvider(ctx: ExecutionContext): Promise<ExecutionResult> {
|
|
11
|
+
const runId = ctx.runId ?? uuidv7();
|
|
12
|
+
let cwd = ctx.cwd ?? process.cwd();
|
|
13
|
+
const config = ctx.config ?? {};
|
|
14
|
+
const startedAt = new Date().toISOString();
|
|
15
|
+
const startMs = Date.now();
|
|
16
|
+
const command = config.command;
|
|
17
|
+
|
|
18
|
+
ctx.onLifecycle?.({ phase: "preparing", step: "binary" });
|
|
19
|
+
if (!command) {
|
|
20
|
+
const errorMessage = 'Process provider requires config.command to be set.';
|
|
21
|
+
ctx.onLifecycle?.({ phase: "error", message: errorMessage });
|
|
22
|
+
return {
|
|
23
|
+
runId,
|
|
24
|
+
exitCode: null,
|
|
25
|
+
signal: null,
|
|
26
|
+
status: "failed" as const,
|
|
27
|
+
startedAt,
|
|
28
|
+
completedAt: new Date().toISOString(),
|
|
29
|
+
durationMs: Date.now() - startMs,
|
|
30
|
+
errorMessage,
|
|
31
|
+
errorCode: "binary_not_found",
|
|
32
|
+
costUsd: null,
|
|
33
|
+
model: null,
|
|
34
|
+
summary: null,
|
|
35
|
+
sessionParams: null,
|
|
36
|
+
sessionDisplayId: null,
|
|
37
|
+
clearSession: false,
|
|
38
|
+
billingType: null,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let resolvedBinary;
|
|
43
|
+
try {
|
|
44
|
+
resolvedBinary = await ensureCommandResolvable(command);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
const errorMessage = err instanceof Error ? err.message : "Command not found";
|
|
47
|
+
ctx.onLifecycle?.({ phase: "error", message: errorMessage });
|
|
48
|
+
return {
|
|
49
|
+
runId,
|
|
50
|
+
exitCode: null,
|
|
51
|
+
signal: null,
|
|
52
|
+
status: "failed" as const,
|
|
53
|
+
startedAt,
|
|
54
|
+
completedAt: new Date().toISOString(),
|
|
55
|
+
durationMs: Date.now() - startMs,
|
|
56
|
+
errorMessage,
|
|
57
|
+
errorCode: "binary_not_found",
|
|
58
|
+
costUsd: null,
|
|
59
|
+
model: null,
|
|
60
|
+
summary: null,
|
|
61
|
+
sessionParams: null,
|
|
62
|
+
sessionDisplayId: null,
|
|
63
|
+
clearSession: false,
|
|
64
|
+
billingType: null,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Workspace isolation
|
|
69
|
+
let workspace: PreparedWorkspace | undefined;
|
|
70
|
+
if (config.workspace) {
|
|
71
|
+
ctx.onLifecycle?.({ phase: "preparing", step: "workspace" });
|
|
72
|
+
workspace = await prepareWorkspace(cwd, config.workspace);
|
|
73
|
+
cwd = workspace.cwd;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const env = buildEnv(ctx.env);
|
|
77
|
+
ensurePathInEnv(env);
|
|
78
|
+
|
|
79
|
+
// Resolve instructions
|
|
80
|
+
ctx.onLifecycle?.({ phase: "preparing", step: "instructions" });
|
|
81
|
+
const instructions = await resolveInstructions(config.instructionsFile);
|
|
82
|
+
const fullPrompt = instructions ? `${instructions}\n\n${ctx.prompt}` : ctx.prompt;
|
|
83
|
+
|
|
84
|
+
ctx.onLifecycle?.({ phase: "spawning" });
|
|
85
|
+
const proc = await runChildProcess({
|
|
86
|
+
runId,
|
|
87
|
+
command: resolvedBinary.bin,
|
|
88
|
+
args: [...resolvedBinary.prefixArgs, ...(config.extraArgs ?? [])],
|
|
89
|
+
cwd,
|
|
90
|
+
env,
|
|
91
|
+
stdin: fullPrompt,
|
|
92
|
+
timeoutSec: config.timeoutSec,
|
|
93
|
+
graceSec: config.graceSec,
|
|
94
|
+
onStart: (pid) => {
|
|
95
|
+
ctx.onLifecycle?.({ phase: "running", pid });
|
|
96
|
+
ctx.onStart?.(pid);
|
|
97
|
+
},
|
|
98
|
+
signal: ctx.signal,
|
|
99
|
+
onOutput: ctx.onOutput ? async (stream, chunk) => {
|
|
100
|
+
try { await ctx.onOutput!(stream, chunk); } catch { /* swallow */ }
|
|
101
|
+
} : undefined,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const errorCode = deriveErrorCode(proc);
|
|
105
|
+
const errorMessage = (() => {
|
|
106
|
+
if (proc.timedOut) return `Timed out after ${config.timeoutSec ?? 0}s`;
|
|
107
|
+
if ((proc.exitCode ?? 0) !== 0) {
|
|
108
|
+
const stderrLine = proc.stderr.split(/\r?\n/).map((l) => l.trim()).find(Boolean);
|
|
109
|
+
return stderrLine ?? `Process exited with code ${proc.exitCode ?? -1}`;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
})();
|
|
113
|
+
|
|
114
|
+
const completedAt = new Date().toISOString();
|
|
115
|
+
const status = proc.aborted ? "aborted" as const
|
|
116
|
+
: proc.timedOut ? "timeout" as const
|
|
117
|
+
: (errorCode || errorMessage) ? "failed" as const
|
|
118
|
+
: "completed" as const;
|
|
119
|
+
|
|
120
|
+
if (status === "completed") {
|
|
121
|
+
ctx.onLifecycle?.({ phase: "completed" });
|
|
122
|
+
} else if (status === "aborted") {
|
|
123
|
+
ctx.onLifecycle?.({ phase: "cancelled" });
|
|
124
|
+
} else {
|
|
125
|
+
ctx.onLifecycle?.({ phase: "error", message: errorMessage ?? "Unknown error" });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
runId,
|
|
130
|
+
exitCode: proc.exitCode,
|
|
131
|
+
signal: proc.signal ?? null,
|
|
132
|
+
status,
|
|
133
|
+
startedAt,
|
|
134
|
+
completedAt,
|
|
135
|
+
durationMs: new Date(completedAt).getTime() - startMs,
|
|
136
|
+
errorMessage,
|
|
137
|
+
errorCode,
|
|
138
|
+
costUsd: null,
|
|
139
|
+
model: null,
|
|
140
|
+
summary: proc.stdout.trim() || null,
|
|
141
|
+
sessionParams: null,
|
|
142
|
+
sessionDisplayId: null,
|
|
143
|
+
clearSession: false,
|
|
144
|
+
billingType: null,
|
|
145
|
+
raw: null,
|
|
146
|
+
workspace,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AuthResolveContext, AuthReport, ProviderModule } from "../../types.js";
|
|
2
|
+
import { ensureCommandResolvable } from "../../utils/binary.js";
|
|
3
|
+
|
|
4
|
+
async function resolveProcessAuth(ctx?: AuthResolveContext): Promise<AuthReport> {
|
|
5
|
+
// Process is a generic runner — the "binary" is user-supplied via config.command.
|
|
6
|
+
// If a command is passed via ctx.command, probe it; otherwise report "unknown".
|
|
7
|
+
if (!ctx?.command) {
|
|
8
|
+
return {
|
|
9
|
+
providerType: "process",
|
|
10
|
+
binary: { installed: false, error: "No command specified (process provider requires config.command)" },
|
|
11
|
+
options: [],
|
|
12
|
+
source: "filesystem",
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const resolved = await ensureCommandResolvable(ctx.command);
|
|
17
|
+
return {
|
|
18
|
+
providerType: "process",
|
|
19
|
+
binary: { installed: true, resolvedPath: resolved.bin },
|
|
20
|
+
options: [],
|
|
21
|
+
source: "filesystem",
|
|
22
|
+
};
|
|
23
|
+
} catch (err) {
|
|
24
|
+
return {
|
|
25
|
+
providerType: "process",
|
|
26
|
+
binary: { installed: false, error: err instanceof Error ? err.message : String(err) },
|
|
27
|
+
options: [],
|
|
28
|
+
source: "filesystem",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const processProvider: ProviderModule = {
|
|
34
|
+
type: "process",
|
|
35
|
+
capabilities: {
|
|
36
|
+
sessions: false,
|
|
37
|
+
modelDiscovery: false,
|
|
38
|
+
quotaProbing: false,
|
|
39
|
+
mcp: false,
|
|
40
|
+
skills: false,
|
|
41
|
+
instructions: true,
|
|
42
|
+
workspace: true,
|
|
43
|
+
planMode: false,
|
|
44
|
+
concurrentSend: false,
|
|
45
|
+
cancelQueuedMessage: false,
|
|
46
|
+
stopTask: false,
|
|
47
|
+
modes: false,
|
|
48
|
+
},
|
|
49
|
+
// execute.ts loads lazily on first use.
|
|
50
|
+
execute: async (ctx) => (await import("./execute.js")).executeProcessProvider(ctx),
|
|
51
|
+
resolveAuth: resolveProcessAuth,
|
|
52
|
+
};
|
package/src/registry.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ProviderModule } from "./types.js";
|
|
2
|
+
import { claudeProvider } from "./providers/claude/index.js";
|
|
3
|
+
import { codexProvider } from "./providers/codex/index.js";
|
|
4
|
+
import { openclawProvider } from "./providers/openclaw/index.js";
|
|
5
|
+
import { processProvider } from "./providers/process/index.js";
|
|
6
|
+
import { geminiProvider } from "./providers/gemini/index.js";
|
|
7
|
+
import { cursorProvider } from "./providers/cursor/index.js";
|
|
8
|
+
import { opencodeProvider } from "./providers/opencode/index.js";
|
|
9
|
+
import { piProvider } from "./providers/pi/index.js";
|
|
10
|
+
import { copilotProvider } from "./providers/copilot/index.js";
|
|
11
|
+
|
|
12
|
+
const providers = new Map<string, ProviderModule>();
|
|
13
|
+
|
|
14
|
+
// Pre-register built-in providers
|
|
15
|
+
providers.set("claude", claudeProvider);
|
|
16
|
+
providers.set("codex", codexProvider);
|
|
17
|
+
providers.set("openclaw", openclawProvider);
|
|
18
|
+
providers.set("process", processProvider);
|
|
19
|
+
providers.set("gemini", geminiProvider);
|
|
20
|
+
providers.set("cursor", cursorProvider);
|
|
21
|
+
providers.set("opencode", opencodeProvider);
|
|
22
|
+
providers.set("pi", piProvider);
|
|
23
|
+
providers.set("copilot", copilotProvider);
|
|
24
|
+
|
|
25
|
+
export function getProvider(type: string): ProviderModule {
|
|
26
|
+
const provider = providers.get(type);
|
|
27
|
+
if (!provider) {
|
|
28
|
+
const available = [...providers.keys()].join(", ");
|
|
29
|
+
throw new Error(`Unknown provider type "${type}". Available: ${available}`);
|
|
30
|
+
}
|
|
31
|
+
return provider;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function listProviders(): string[] {
|
|
35
|
+
return [...providers.keys()];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function registerProvider(provider: ProviderModule): void {
|
|
39
|
+
providers.set(provider.type, provider);
|
|
40
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { SessionRecord } from "../types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Current `SessionRecord.version`. Bumped only on a breaking shape change;
|
|
5
|
+
* `isSessionRecord` / `assertSessionRecord` validate against it so a host can
|
|
6
|
+
* detect a record written by an incompatible library version.
|
|
7
|
+
*/
|
|
8
|
+
export const SESSION_RECORD_VERSION = 1 as const;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Thrown when a value that should be a `SessionRecord` is malformed. Branch on
|
|
12
|
+
* it; don't parse. Mirrors `MalformedProviderConfigError` — `path` names the
|
|
13
|
+
* offending field when known.
|
|
14
|
+
*/
|
|
15
|
+
export class MalformedSessionRecordError extends Error {
|
|
16
|
+
readonly path?: string;
|
|
17
|
+
constructor(message: string, path?: string) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "MalformedSessionRecordError";
|
|
20
|
+
this.path = path;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Inputs for {@link createSessionRecord}. */
|
|
25
|
+
export interface CreateSessionRecordInput {
|
|
26
|
+
/** Provider type (registry key) that owns this session. */
|
|
27
|
+
providerType: string;
|
|
28
|
+
/** Codec-serialized session params (e.g. `{sessionId, cwd?}`). */
|
|
29
|
+
params: Record<string, unknown>;
|
|
30
|
+
/** Working directory the session ran in — transcript-lookup hint. */
|
|
31
|
+
cwd?: string | null;
|
|
32
|
+
/** Human-facing id (`sessionCodec.getDisplayId`), for UIs/logs. */
|
|
33
|
+
displayId?: string | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Build a `SessionRecord`, stamping `version` and a fresh `updatedAt`. This is
|
|
38
|
+
* the one blessed way to mint a record outside of `session.describe()`.
|
|
39
|
+
*/
|
|
40
|
+
export function createSessionRecord(input: CreateSessionRecordInput): SessionRecord {
|
|
41
|
+
return {
|
|
42
|
+
version: SESSION_RECORD_VERSION,
|
|
43
|
+
providerType: input.providerType,
|
|
44
|
+
params: input.params,
|
|
45
|
+
cwd: input.cwd ?? null,
|
|
46
|
+
displayId: input.displayId ?? null,
|
|
47
|
+
updatedAt: new Date().toISOString(),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
52
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Structural guard for a `SessionRecord`. Tolerant of extra keys (a host may
|
|
57
|
+
* persist the record inside a larger row and read it back). Does not mutate.
|
|
58
|
+
*/
|
|
59
|
+
export function isSessionRecord(v: unknown): v is SessionRecord {
|
|
60
|
+
if (!isPlainObject(v)) return false;
|
|
61
|
+
if (v["version"] !== SESSION_RECORD_VERSION) return false;
|
|
62
|
+
if (typeof v["providerType"] !== "string" || v["providerType"].length === 0) return false;
|
|
63
|
+
if (!isPlainObject(v["params"])) return false;
|
|
64
|
+
if (v["cwd"] !== null && typeof v["cwd"] !== "string") return false;
|
|
65
|
+
if (v["displayId"] !== null && typeof v["displayId"] !== "string") return false;
|
|
66
|
+
if (typeof v["updatedAt"] !== "string") return false;
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Assert `v` is a well-formed `SessionRecord`, narrowing the type. Throws
|
|
72
|
+
* `MalformedSessionRecordError` naming the first offending field.
|
|
73
|
+
*/
|
|
74
|
+
export function assertSessionRecord(v: unknown): asserts v is SessionRecord {
|
|
75
|
+
if (!isPlainObject(v)) {
|
|
76
|
+
throw new MalformedSessionRecordError("session record must be an object");
|
|
77
|
+
}
|
|
78
|
+
if (v["version"] !== SESSION_RECORD_VERSION) {
|
|
79
|
+
throw new MalformedSessionRecordError(
|
|
80
|
+
`session record version must be ${SESSION_RECORD_VERSION}; got ${JSON.stringify(v["version"])}`,
|
|
81
|
+
"version",
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (typeof v["providerType"] !== "string" || v["providerType"].length === 0) {
|
|
85
|
+
throw new MalformedSessionRecordError(
|
|
86
|
+
"session record providerType must be a non-empty string",
|
|
87
|
+
"providerType",
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (!isPlainObject(v["params"])) {
|
|
91
|
+
throw new MalformedSessionRecordError("session record params must be an object", "params");
|
|
92
|
+
}
|
|
93
|
+
if (v["cwd"] !== null && typeof v["cwd"] !== "string") {
|
|
94
|
+
throw new MalformedSessionRecordError("session record cwd must be a string or null", "cwd");
|
|
95
|
+
}
|
|
96
|
+
if (v["displayId"] !== null && typeof v["displayId"] !== "string") {
|
|
97
|
+
throw new MalformedSessionRecordError(
|
|
98
|
+
"session record displayId must be a string or null",
|
|
99
|
+
"displayId",
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (typeof v["updatedAt"] !== "string") {
|
|
103
|
+
throw new MalformedSessionRecordError(
|
|
104
|
+
"session record updatedAt must be an ISO timestamp string",
|
|
105
|
+
"updatedAt",
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|