@alpacakit/agents 0.1.0-beta.37 → 0.1.0-beta.39

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.
@@ -1 +1 @@
1
- {"version":3,"file":"host-controller.d.ts","sourceRoot":"","sources":["../../../src/internal/controlled-process/host-controller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EAErB,6BAA6B,EAC9B,MAAM,kCAAkC,CAAC;AAQ1C,OAAO,KAAK,EAAY,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA6BtE,OAAO,KAAK,EACV,uBAAuB,EAEvB,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAqC/B,MAAM,MAAM,4BAA4B,GAAG;IACzC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC;CAC3C,CAAC;AAEF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACrD,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACvD;AAsED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,2BAA2B,CAAC,CAoJtC;AAgnBD,wBAAsB,wBAAwB,CAAC,KAAK,EAAE;IACpD,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,qBAAqB,EAAE,kBAAkB,CAAC,CAAC;CACrE,GAAG,OAAO,CAAC,6BAA6B,CAAC,CA4BzC"}
1
+ {"version":3,"file":"host-controller.d.ts","sourceRoot":"","sources":["../../../src/internal/controlled-process/host-controller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EAErB,6BAA6B,EAC9B,MAAM,kCAAkC,CAAC;AAQ1C,OAAO,KAAK,EAAY,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA6BtE,OAAO,KAAK,EACV,uBAAuB,EAEvB,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAqC/B,MAAM,MAAM,4BAA4B,GAAG;IACzC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC;CAC3C,CAAC;AAEF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACrD,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACvD;AAsED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,2BAA2B,CAAC,CAoJtC;AA6oBD,wBAAsB,wBAAwB,CAAC,KAAK,EAAE;IACpD,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,qBAAqB,EAAE,kBAAkB,CAAC,CAAC;CACrE,GAAG,OAAO,CAAC,6BAA6B,CAAC,CA4BzC"}
@@ -366,7 +366,17 @@ async function requestTerminationBeforeDeadline(input) {
366
366
  if (raced.kind === "closed") {
367
367
  return null;
368
368
  }
369
- if (raced.kind === "failure" || raced.kind === "deadline") {
369
+ if (raced.kind === "deadline") {
370
+ throw raced.error;
371
+ }
372
+ if (raced.kind === "failure") {
373
+ // A supervisor that rolled back before reporting its launch failure has
374
+ // already unlinked its socket, so this request can fail while its close is
375
+ // the only remaining evidence. Join that close within the same deadline;
376
+ // the caller still has to prove absence from the signed record.
377
+ if (await closedBeforeDeadline(input.supervisorClose, input.deadline)) {
378
+ return null;
379
+ }
370
380
  throw raced.error;
371
381
  }
372
382
  if (raced.response.kind !== SUPERVISOR_RESPONSE_KIND.termination) {
@@ -379,6 +389,18 @@ async function requestTerminationBeforeDeadline(input) {
379
389
  supervisorExit: null,
380
390
  };
381
391
  }
392
+ function closedBeforeDeadline(supervisorClose, deadline) {
393
+ if (supervisorClose.didClose()) {
394
+ return Promise.resolve(true);
395
+ }
396
+ return new Promise((resolve) => {
397
+ const timeout = setTimeout(() => resolve(false), Math.max(0, deadline - Date.now()));
398
+ void supervisorClose.promise.then(() => {
399
+ clearTimeout(timeout);
400
+ resolve(true);
401
+ });
402
+ });
403
+ }
382
404
  async function raceSupervisorRequestWithClose(input) {
383
405
  if (input.supervisorClose.didClose()) {
384
406
  return {
@@ -238,7 +238,6 @@ async function runSupervisorSession(input) {
238
238
  shimControl = shim.stdio[SHIM_CONTROL_FD];
239
239
  shimStatus = shim.stdio[SHIM_STATUS_FD];
240
240
  shimInput = shim.stdio.at(SHIM_INPUT_FD);
241
- shimInput.on("error", () => { });
242
241
  const pid = requireShimPid(shim);
243
242
  shimPid = pid;
244
243
  const record = {
@@ -701,9 +700,23 @@ function spawnShim(bootstrap) {
701
700
  env: {},
702
701
  stdio: ["ignore", "pipe", "pipe", "pipe", "pipe", "pipe"],
703
702
  });
703
+ containShimStdioErrors(shim);
704
704
  shim.once("error", () => { });
705
705
  return shim;
706
706
  }
707
+ /**
708
+ * The supervisor owns every shim stdio error from spawn until the shim closes.
709
+ * Framed writes, status reads, and output pumps keep their own listeners and
710
+ * remain responsible for their outcomes; this owner only contains the pipe
711
+ * errors a dying shim raises once those operations have settled, so a late
712
+ * reset can never end supervisor custody before its failure is reported.
713
+ */
714
+ function containShimStdioErrors(shim) {
715
+ for (const stream of shim.stdio) {
716
+ stream?.on("error", consumeShimStdioError);
717
+ }
718
+ }
719
+ function consumeShimStdioError() { }
707
720
  function waitForShimClose(shim) {
708
721
  return new Promise((resolve) => {
709
722
  shim.once("close", () => resolve());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alpacakit/agents",
3
- "version": "0.1.0-beta.37",
3
+ "version": "0.1.0-beta.39",
4
4
  "description": "Run AI coding agents programmatically: execute, stream, classify failures, fail over.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "zod": "^4.3.6",
48
- "@alpacakit/core": "0.1.0-beta.37"
48
+ "@alpacakit/core": "0.1.0-beta.39"
49
49
  },
50
50
  "devDependencies": {
51
51
  "typescript": "^5.9.3",
@@ -1,14 +0,0 @@
1
- /**
2
- * Minimal file-backed profile store (A11): rows follow rule 2 and never
3
- * contain secret values (A9) — materials carry refs and paths only.
4
- * Profile management UX is consumer territory. Unlike fleet state, this
5
- * is durable user data: malformed or unknown-version files are typed
6
- * errors, never silent resets.
7
- */
8
- import { type AuthProfile } from "./material.js";
9
- export interface ProfileStore {
10
- list(): Promise<AuthProfile[]>;
11
- save(profiles: AuthProfile[]): Promise<void>;
12
- }
13
- export declare function fileProfileStore(filePath: string): ProfileStore;
14
- //# sourceMappingURL=profile-store.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"profile-store.d.ts","sourceRoot":"","sources":["../../src/auth/profile-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAcrE,MAAM,WAAW,YAAY;IAC3B,IAAI,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/B,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AA+FD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAE/D"}
@@ -1,102 +0,0 @@
1
- /**
2
- * Minimal file-backed profile store (A11): rows follow rule 2 and never
3
- * contain secret values (A9) — materials carry refs and paths only.
4
- * Profile management UX is consumer territory. Unlike fleet state, this
5
- * is durable user data: malformed or unknown-version files are typed
6
- * errors, never silent resets.
7
- */
8
- import { readFile } from "node:fs/promises";
9
- import { AGENT_ERROR_CODE, AgentError } from "../core/error.js";
10
- import { isErrnoCode, writeFileAtomically } from "../core/fs.js";
11
- import { readUnknownRecord } from "../core/json.js";
12
- import { TEXT_ENCODING } from "../core/values.js";
13
- import { AUTH_MATERIAL_KIND } from "./material.js";
14
- const PROFILE_FILE_VERSION = 1;
15
- const PROFILE_STORE_ERROR_REASON = {
16
- unreadable: "unreadable file",
17
- malformedJson: "malformed JSON",
18
- rootMustBeObject: "root must be an object",
19
- unsupportedVersion: "unsupported version",
20
- invalidProfiles: "profiles must be auth profile records",
21
- };
22
- function profileStoreError(reason, cause) {
23
- return new AgentError({
24
- code: AGENT_ERROR_CODE.invalidInput,
25
- classification: AgentError.agentFailure(AGENT_ERROR_CODE.invalidInput),
26
- message: `Invalid auth profile store: ${reason}`,
27
- data: { reason },
28
- ...(cause === undefined ? {} : { cause }),
29
- });
30
- }
31
- function isSecretRef(value) {
32
- const record = readUnknownRecord(value);
33
- if (record === null) {
34
- return false;
35
- }
36
- const { resolver, id } = record;
37
- return typeof resolver === "string" && typeof id === "string";
38
- }
39
- function isAuthProfile(value) {
40
- const record = readUnknownRecord(value);
41
- if (record === null) {
42
- return false;
43
- }
44
- const { id, agentId, material: materialValue } = record;
45
- const material = readUnknownRecord(materialValue);
46
- if (material === null) {
47
- return false;
48
- }
49
- const { kind, path, secret } = material;
50
- const materialValid = (kind === AUTH_MATERIAL_KIND.homeDir && typeof path === "string") ||
51
- (kind === AUTH_MATERIAL_KIND.apiKey && isSecretRef(secret));
52
- return typeof id === "string" && typeof agentId === "string" && materialValid;
53
- }
54
- function parseProfileFile(raw) {
55
- let parsed;
56
- try {
57
- parsed = JSON.parse(raw);
58
- }
59
- catch (error) {
60
- throw profileStoreError(PROFILE_STORE_ERROR_REASON.malformedJson, error);
61
- }
62
- const root = readUnknownRecord(parsed);
63
- if (root === null) {
64
- throw profileStoreError(PROFILE_STORE_ERROR_REASON.rootMustBeObject);
65
- }
66
- const { version, profiles } = root;
67
- if (version !== PROFILE_FILE_VERSION) {
68
- throw profileStoreError(PROFILE_STORE_ERROR_REASON.unsupportedVersion);
69
- }
70
- if (!Array.isArray(profiles) || !profiles.every(isAuthProfile)) {
71
- throw profileStoreError(PROFILE_STORE_ERROR_REASON.invalidProfiles);
72
- }
73
- return profiles;
74
- }
75
- class FileProfileStore {
76
- filePath;
77
- constructor(filePath) {
78
- this.filePath = filePath;
79
- }
80
- async list() {
81
- let raw;
82
- try {
83
- raw = await readFile(this.filePath, TEXT_ENCODING);
84
- }
85
- catch (error) {
86
- // Durable user data: only "not created yet" reads as empty; any
87
- // other read failure surfaces instead of masking real profiles.
88
- if (isErrnoCode(error, "ENOENT")) {
89
- return [];
90
- }
91
- throw profileStoreError(PROFILE_STORE_ERROR_REASON.unreadable, error);
92
- }
93
- return parseProfileFile(raw);
94
- }
95
- async save(profiles) {
96
- const file = { version: PROFILE_FILE_VERSION, profiles };
97
- await writeFileAtomically(this.filePath, `${JSON.stringify(file, null, 2)}\n`);
98
- }
99
- }
100
- export function fileProfileStore(filePath) {
101
- return new FileProfileStore(filePath);
102
- }
@@ -1,2 +0,0 @@
1
- export { keyringResolver, type KeyringResolverOptions, } from "./resolver.js";
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/keyring/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,KAAK,sBAAsB,GAC5B,MAAM,eAAe,CAAC"}
@@ -1 +0,0 @@
1
- export { keyringResolver, } from "./resolver.js";
@@ -1,9 +0,0 @@
1
- import type { SecretResolver } from "../core/ports.js";
2
- export type KeyringResolverOptions = {
3
- readonly service: string;
4
- };
5
- type KeyringModuleLoader = () => Promise<unknown>;
6
- export declare function keyringResolver(options: KeyringResolverOptions): SecretResolver;
7
- export declare function createKeyringResolver(options: KeyringResolverOptions, loader: KeyringModuleLoader): SecretResolver;
8
- export {};
9
- //# sourceMappingURL=resolver.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/keyring/resolver.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,KAAK,mBAAmB,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;AAiBlD,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,cAAc,CAEhB;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,EAC/B,MAAM,EAAE,mBAAmB,GAC1B,cAAc,CA4BhB"}
@@ -1,64 +0,0 @@
1
- // biome-ignore-all lint/style/useNamingConvention: Keyring peer exports mirror upstream PascalCase names.
2
- import { AGENT_ERROR_CODE, AgentError, NOT_AVAILABLE_CAUSE, } from "../core/error.js";
3
- const KEYRING_PEER_PACKAGE = "@napi-rs/keyring";
4
- const KEYRING_ENTRY_EXPORT = "Entry";
5
- const EMPTY_SECRET_VALUE = "";
6
- const defaultKeyringModuleLoader = () => import(KEYRING_PEER_PACKAGE);
7
- export function keyringResolver(options) {
8
- return createKeyringResolver(options, defaultKeyringModuleLoader);
9
- }
10
- export function createKeyringResolver(options, loader) {
11
- const service = options.service;
12
- let modulePromise = null;
13
- const load = () => {
14
- modulePromise ??= loadKeyringModule(loader);
15
- return modulePromise;
16
- };
17
- return {
18
- resolve: async (id) => {
19
- const module = await load();
20
- try {
21
- // Absent entries are the `null` return (`Entry.getPassword():
22
- // string | null` in @napi-rs/keyring); anything thrown is a real
23
- // keychain failure, never "missing secret".
24
- const value = await new module.Entry(service, id).getPassword();
25
- return value === EMPTY_SECRET_VALUE ? null : value;
26
- }
27
- catch (error) {
28
- throw new AgentError({
29
- code: AGENT_ERROR_CODE.failed,
30
- classification: AgentError.agentFailure(AGENT_ERROR_CODE.failed),
31
- message: `Keyring secret "${id}" could not be read`,
32
- data: { exitCode: null, stdoutTail: null, stderrTail: null },
33
- cause: error,
34
- });
35
- }
36
- },
37
- };
38
- }
39
- async function loadKeyringModule(loader) {
40
- let loaded;
41
- try {
42
- loaded = await loader();
43
- }
44
- catch (error) {
45
- throw keyringNotInstalledError(error);
46
- }
47
- if (typeof loaded !== "object" || loaded === null) {
48
- throw keyringNotInstalledError(null);
49
- }
50
- const entry = Reflect.get(loaded, KEYRING_ENTRY_EXPORT);
51
- if (typeof entry !== "function") {
52
- throw keyringNotInstalledError(null);
53
- }
54
- return { Entry: entry };
55
- }
56
- function keyringNotInstalledError(cause) {
57
- return new AgentError({
58
- code: AGENT_ERROR_CODE.notAvailable,
59
- classification: AgentError.agentFailure(AGENT_ERROR_CODE.notAvailable),
60
- message: "Keyring peer is not installed",
61
- data: { cause: NOT_AVAILABLE_CAUSE.notInstalled },
62
- cause,
63
- });
64
- }