@gobing-ai/ts-ai-runner 0.5.4 → 0.5.7

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.
@@ -23,6 +23,13 @@ export declare class ValueError extends Error {
23
23
  }
24
24
  /** Validate that `id` matches the agent ID format: 2-64 chars, lowercase alphanumeric with `_` or `-`. Returns the id on success, throws `ValueError` otherwise. */
25
25
  export declare function validateAgentId(id: string): string;
26
+ /**
27
+ * Validate a caller-supplied session id before it is used in a filesystem path
28
+ * (task 0086 R15): 1-128 chars, starting alphanumeric, then alnum/`.`/`_`/`-`,
29
+ * and never containing `..`. Returns the id on success, throws `ValueError`
30
+ * otherwise — failing closed beats sanitizing attacker-controlled path input.
31
+ */
32
+ export declare function assertSafeSessionId(id: string): string;
26
33
  /** Load and validate all YAML agent spec files from `configDir`. Throws `ValueError` on parse failures or duplicate IDs. */
27
34
  export declare function loadAgentSpecs(configDir: string, fs?: FileSystem): Promise<AgentSpec[]>;
28
35
  /** Serialize `spec` to YAML and write it to `configDir/<id>.yaml`. Creates the directory if missing. */
@@ -1 +1 @@
1
- {"version":3,"file":"agent-spec.d.ts","sourceRoot":"","sources":["../src/agent-spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,UAAU,EAIlB,MAAM,uBAAuB,CAAC;AAE/B,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,4EAA4E;AAC5E,qBAAa,UAAW,SAAQ,KAAK;gBACrB,OAAO,EAAE,MAAM;CAI9B;AAED,oKAAoK;AACpK,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED,4HAA4H;AAC5H,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAE,UAAmC,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAcrH;AAED,wGAAwG;AACxG,wBAAsB,aAAa,CAC/B,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,MAAM,EACjB,EAAE,GAAE,UAAmC,GACxC,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,oGAAoG;AACpG,wBAAsB,eAAe,CACjC,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,EACjB,EAAE,GAAE,UAAmC,GACxC,OAAO,CAAC,IAAI,CAAC,CAGf"}
1
+ {"version":3,"file":"agent-spec.d.ts","sourceRoot":"","sources":["../src/agent-spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,UAAU,EAIlB,MAAM,uBAAuB,CAAC;AAE/B,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,4EAA4E;AAC5E,qBAAa,UAAW,SAAQ,KAAK;gBACrB,OAAO,EAAE,MAAM;CAI9B;AAED,oKAAoK;AACpK,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED,4HAA4H;AAC5H,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAE,UAAmC,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAcrH;AAED,wGAAwG;AACxG,wBAAsB,aAAa,CAC/B,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,MAAM,EACjB,EAAE,GAAE,UAAmC,GACxC,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,oGAAoG;AACpG,wBAAsB,eAAe,CACjC,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,EACjB,EAAE,GAAE,UAAmC,GACxC,OAAO,CAAC,IAAI,CAAC,CAGf"}
@@ -13,6 +13,18 @@ export function validateAgentId(id) {
13
13
  }
14
14
  return id;
15
15
  }
16
+ /**
17
+ * Validate a caller-supplied session id before it is used in a filesystem path
18
+ * (task 0086 R15): 1-128 chars, starting alphanumeric, then alnum/`.`/`_`/`-`,
19
+ * and never containing `..`. Returns the id on success, throws `ValueError`
20
+ * otherwise — failing closed beats sanitizing attacker-controlled path input.
21
+ */
22
+ export function assertSafeSessionId(id) {
23
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(id) || id.includes('..')) {
24
+ throw new ValueError(`Invalid session id "${id}": expected 1-128 chars (alphanumeric, ".", "_", "-", starting alphanumeric) without path separators or ".."`);
25
+ }
26
+ return id;
27
+ }
16
28
  /** Load and validate all YAML agent spec files from `configDir`. Throws `ValueError` on parse failures or duplicate IDs. */
17
29
  export async function loadAgentSpecs(configDir, fs = createNodeFileSystem()) {
18
30
  const entries = (await safeReadDir(configDir, fs))
@@ -1 +1 @@
1
- {"version":3,"file":"shims.d.ts","sourceRoot":"","sources":["../../src/agents/shims.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shims.d.ts","sourceRoot":"","sources":["../../src/agents/shims.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { getLogger } from '@gobing-ai/ts-infra';
2
2
  import { joinPath } from '@gobing-ai/ts-runtime';
3
+ import { assertSafeSessionId } from '../agent-spec.js';
3
4
  /** pi-family rpc dialect (pi docs/rpc.md): one JSON object per line. */
4
5
  const piRpcFrame = (input) => `${JSON.stringify({ type: 'prompt', message: input })}\n`;
5
6
  /** claude stream-json input dialect: one user-message JSONL envelope per line. */
@@ -353,15 +354,18 @@ const fmShim = {
353
354
  // and suppress any unscoped continue. `continue` alone degrades to a
354
355
  // fresh call — fm has no implicit last-session flag.
355
356
  if (options.sessionId !== undefined) {
356
- const file = options.sessionDir !== undefined
357
- ? joinPath(options.sessionDir, `${options.sessionId}.json`)
358
- : `${options.sessionId}.json`;
357
+ // Task 0086 R15: sessionId lands in a filesystem path — validate it
358
+ // before it can escape sessionDir via separators or traversal.
359
+ const id = assertSafeSessionId(options.sessionId);
360
+ const file = options.sessionDir !== undefined ? joinPath(options.sessionDir, `${id}.json`) : `${id}.json`;
359
361
  args.push('--resume', file, '--save-transcript', file);
360
362
  }
361
363
  else if (options.sessionDir !== undefined) {
362
364
  args.push('--save-transcript', joinPath(options.sessionDir, 'fm-session.json'));
363
365
  }
364
- args.push(options.input ?? '');
366
+ // Task 0086 R4: `--` pins the input as a positional, so dash-leading
367
+ // prompts (--help, -n) are never reparsed as fm flags.
368
+ args.push('--', options.input ?? '');
365
369
  return { command: 'fm', args };
366
370
  },
367
371
  getAuthCommand: () => ({ command: 'fm', args: ['available', '--model', 'system'] }),
@@ -40,6 +40,14 @@ export interface DecisionMakerOptions {
40
40
  fetch?: typeof fetch;
41
41
  [key: string]: unknown;
42
42
  }
43
+ /**
44
+ * @internal Exported for task 0086 R9 branch tests: `importModule` lets tests
45
+ * simulate package-import failures that cannot occur in-workspace (workspace
46
+ * links always resolve).
47
+ */
48
+ export declare function resolveLayaDriver(options: DecisionMakerOptions, importModule?: (specifier: string) => Promise<unknown>): Promise<DecisionDriver>;
49
+ /** @internal Exported for task 0086 R9 branch tests; see {@link resolveLayaDriver}. */
50
+ export declare function resolveFmDriver(options: DecisionMakerOptions, importModule?: (specifier: string) => Promise<unknown>): Promise<DecisionDriver>;
43
51
  /**
44
52
  * Build a DecisionMaker. `options.driver` wins when supplied; otherwise `options.backend`
45
53
  * resolves the driver (default `'typesafe'`). The driver is constructed lazily on first use.
@@ -1 +1 @@
1
- {"version":3,"file":"decision-maker.d.ts","sourceRoot":"","sources":["../../src/decision/decision-maker.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACR,UAAU,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,WAAW,EACd,MAAM,SAAS,CAAC;AAKjB,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC1B,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sFAAsF;IACtF,GAAG,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE;QAC/C,KAAK,EAAE,aAAa,CAAC;QACrB,SAAS,EAAE,CAAC,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,yEAAyE;IACzE,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EACzB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,GACxB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5B,gFAAgF;IAChF,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1G,wEAAwE;IACxE,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,IAAI,CAAC;QAAC,EAAE,CAAC,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACxG;AAED,gEAAgE;AAChE,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,CAAC;AAErE,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACjC,oHAAoH;IACpH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,4GAA4G;IAC5G,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,qFAAqF;IACrF,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AA0DD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAwDrF"}
1
+ {"version":3,"file":"decision-maker.d.ts","sourceRoot":"","sources":["../../src/decision/decision-maker.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACR,UAAU,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,WAAW,EACd,MAAM,SAAS,CAAC;AAKjB,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC1B,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sFAAsF;IACtF,GAAG,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE;QAC/C,KAAK,EAAE,aAAa,CAAC;QACrB,SAAS,EAAE,CAAC,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,yEAAyE;IACzE,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EACzB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,GACxB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5B,gFAAgF;IAChF,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1G,wEAAwE;IACxE,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,IAAI,CAAC;QAAC,EAAE,CAAC,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACxG;AAED,gEAAgE;AAChE,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,CAAC;AAErE,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACjC,oHAAoH;IACpH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,4GAA4G;IAC5G,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,qFAAqF;IACrF,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAoBD;;;;GAIG;AACH,wBAAsB,iBAAiB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,YAAY,GAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAoC,GACzF,OAAO,CAAC,cAAc,CAAC,CAuBzB;AAID,uFAAuF;AACvF,wBAAsB,eAAe,CACjC,OAAO,EAAE,oBAAoB,EAC7B,YAAY,GAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAoC,GACzF,OAAO,CAAC,cAAc,CAAC,CAkBzB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAwDrF"}
@@ -22,30 +22,42 @@ function resolveApiKey(options) {
22
22
  return apiKey;
23
23
  }
24
24
  const LAYA_DRIVER_PACKAGE = '@gobing-ai/ts-laya-mlx';
25
- async function resolveLayaDriver(options) {
25
+ /**
26
+ * @internal Exported for task 0086 R9 branch tests: `importModule` lets tests
27
+ * simulate package-import failures that cannot occur in-workspace (workspace
28
+ * links always resolve).
29
+ */
30
+ export async function resolveLayaDriver(options, importModule = (specifier) => import(specifier)) {
31
+ // Task 0086 R9: only the IMPORT is translated to the install-hint error;
32
+ // construction errors from the factory itself propagate unchanged so the
33
+ // caller sees the real cause instead of a misleading install message.
34
+ let mod;
26
35
  try {
27
- const mod = (await import(LAYA_DRIVER_PACKAGE));
28
- if (typeof mod.createLayaDriver !== 'function') {
29
- throw new Error(`Module '${LAYA_DRIVER_PACKAGE}' does not export createLayaDriver`);
30
- }
31
- return mod.createLayaDriver(options);
36
+ mod = (await importModule(LAYA_DRIVER_PACKAGE));
32
37
  }
33
38
  catch (cause) {
34
39
  throw new DecisionConfigError(`The 'laya-local' backend requires '${LAYA_DRIVER_PACKAGE}' to be installed; install it with 'bun add ${LAYA_DRIVER_PACKAGE}'`, 'LAYA_BACKEND', { cause });
35
40
  }
41
+ if (typeof mod.createLayaDriver !== 'function') {
42
+ throw new DecisionConfigError(`Module '${LAYA_DRIVER_PACKAGE}' does not export createLayaDriver`, 'LAYA_BACKEND');
43
+ }
44
+ return mod.createLayaDriver(options);
36
45
  }
37
46
  const FM_DRIVER_PACKAGE = '@gobing-ai/ts-decision-fm';
38
- async function resolveFmDriver(options) {
47
+ /** @internal Exported for task 0086 R9 branch tests; see {@link resolveLayaDriver}. */
48
+ export async function resolveFmDriver(options, importModule = (specifier) => import(specifier)) {
49
+ // Task 0086 R9: same import/construction split as laya above.
50
+ let mod;
39
51
  try {
40
- const mod = (await import(FM_DRIVER_PACKAGE));
41
- if (typeof mod.createFmDriver !== 'function') {
42
- throw new Error(`Module '${FM_DRIVER_PACKAGE}' does not export createFmDriver`);
43
- }
44
- return mod.createFmDriver(options);
52
+ mod = (await importModule(FM_DRIVER_PACKAGE));
45
53
  }
46
54
  catch (cause) {
47
55
  throw new DecisionConfigError(`The 'fm-local' backend requires '${FM_DRIVER_PACKAGE}' to be installed; install it with 'bun add ${FM_DRIVER_PACKAGE}'`, 'FM_BACKEND', { cause });
48
56
  }
57
+ if (typeof mod.createFmDriver !== 'function') {
58
+ throw new DecisionConfigError(`Module '${FM_DRIVER_PACKAGE}' does not export createFmDriver`, 'FM_BACKEND');
59
+ }
60
+ return mod.createFmDriver(options);
49
61
  }
50
62
  /**
51
63
  * Build a DecisionMaker. `options.driver` wins when supplied; otherwise `options.backend`
@@ -32,7 +32,8 @@ export declare function getGitContext(workspacePath: string, executor?: ProcessE
32
32
  * Query git for the current branch name and dirty file count synchronously.
33
33
  *
34
34
  * @deprecated Use the async {@link getGitContext} instead. Kept for one release for sync callers.
35
- * Defaults to the deprecated {@link BunSyncProcessExecutor}; new code must not rely on it.
35
+ * Defaults to {@link NodeSyncProcessExecutor} (task 0087 R7 migrated the default off the
36
+ * deprecated BunSyncProcessExecutor).
36
37
  */
37
38
  export declare function getGitContextSync(workspacePath: string, executor?: SyncProcessExecutor): string | null;
38
39
  //# sourceMappingURL=identity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAC3B,MAAM,uBAAuB,CAAC;AAE/B,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,8HAA8H;AAC9H,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CA8ClE;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAC/B,aAAa,EAAE,MAAM,EACrB,QAAQ,GAAE,eAAwD,GACnE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASxB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC7B,aAAa,EAAE,MAAM,EACrB,QAAQ,GAAE,mBAAkD,GAC7D,MAAM,GAAG,IAAI,CASf"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAC3B,MAAM,uBAAuB,CAAC;AAE/B,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,8HAA8H;AAC9H,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CA8ClE;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAC/B,aAAa,EAAE,MAAM,EACrB,QAAQ,GAAE,eAAwD,GACnE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASxB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC7B,aAAa,EAAE,MAAM,EACrB,QAAQ,GAAE,mBAAmD,GAC9D,MAAM,GAAG,IAAI,CASf"}
package/dist/identity.js CHANGED
@@ -1,4 +1,4 @@
1
- import { BunSyncProcessExecutor, nodeBunFactory, } from '@gobing-ai/ts-runtime';
1
+ import { NodeSyncProcessExecutor, nodeBunFactory, } from '@gobing-ai/ts-runtime';
2
2
  /** Build a human-readable identity preamble string that describes the agent, its task, peers, guardrails, and git context. */
3
3
  export function buildIdentityPreamble(ctx) {
4
4
  const sections = [
@@ -52,9 +52,10 @@ export async function getGitContext(workspacePath, executor = nodeBunFactory.cre
52
52
  * Query git for the current branch name and dirty file count synchronously.
53
53
  *
54
54
  * @deprecated Use the async {@link getGitContext} instead. Kept for one release for sync callers.
55
- * Defaults to the deprecated {@link BunSyncProcessExecutor}; new code must not rely on it.
55
+ * Defaults to {@link NodeSyncProcessExecutor} (task 0087 R7 migrated the default off the
56
+ * deprecated BunSyncProcessExecutor).
56
57
  */
57
- export function getGitContextSync(workspacePath, executor = new BunSyncProcessExecutor()) {
58
+ export function getGitContextSync(workspacePath, executor = new NodeSyncProcessExecutor()) {
58
59
  const branch = runGitSync(executor, ['-C', workspacePath, 'branch', '--show-current']);
59
60
  if (branch === null || branch === '')
60
61
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-ai-runner",
3
- "version": "0.5.4",
3
+ "version": "0.5.7",
4
4
  "description": "@gobing-ai/ts-ai-runner — Coding-agent shims, detection, doctor checks, and prompt execution.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -47,13 +47,13 @@
47
47
  "release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-ai-runner-v<version> && git push --tags' && exit 1"
48
48
  },
49
49
  "dependencies": {
50
- "@gobing-ai/ts-infra": "^0.5.4",
51
- "@gobing-ai/ts-runtime": "^0.5.4",
50
+ "@gobing-ai/ts-infra": "^0.5.7",
51
+ "@gobing-ai/ts-runtime": "^0.5.7",
52
52
  "@typesafe-ai/sdk": "0.6.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/bun": "1.3.14",
56
- "@gobing-ai/ts-db": "^0.5.4"
56
+ "@gobing-ai/ts-db": "^0.5.7"
57
57
  },
58
58
  "publishConfig": {
59
59
  "access": "public"
package/src/agent-spec.ts CHANGED
@@ -42,6 +42,21 @@ export function validateAgentId(id: string): string {
42
42
  return id;
43
43
  }
44
44
 
45
+ /**
46
+ * Validate a caller-supplied session id before it is used in a filesystem path
47
+ * (task 0086 R15): 1-128 chars, starting alphanumeric, then alnum/`.`/`_`/`-`,
48
+ * and never containing `..`. Returns the id on success, throws `ValueError`
49
+ * otherwise — failing closed beats sanitizing attacker-controlled path input.
50
+ */
51
+ export function assertSafeSessionId(id: string): string {
52
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(id) || id.includes('..')) {
53
+ throw new ValueError(
54
+ `Invalid session id "${id}": expected 1-128 chars (alphanumeric, ".", "_", "-", starting alphanumeric) without path separators or ".."`,
55
+ );
56
+ }
57
+ return id;
58
+ }
59
+
45
60
  /** Load and validate all YAML agent spec files from `configDir`. Throws `ValueError` on parse failures or duplicate IDs. */
46
61
  export async function loadAgentSpecs(configDir: string, fs: FileSystem = createNodeFileSystem()): Promise<AgentSpec[]> {
47
62
  const entries = (await safeReadDir(configDir, fs))
@@ -1,5 +1,6 @@
1
1
  import { getLogger } from '@gobing-ai/ts-infra';
2
2
  import { joinPath } from '@gobing-ai/ts-runtime';
3
+ import { assertSafeSessionId } from '../agent-spec';
3
4
 
4
5
  /** Identifier for one supported coding agent (canonical id). */
5
6
  export type AgentName =
@@ -459,15 +460,17 @@ const fmShim: AgentShim = {
459
460
  // and suppress any unscoped continue. `continue` alone degrades to a
460
461
  // fresh call — fm has no implicit last-session flag.
461
462
  if (options.sessionId !== undefined) {
462
- const file =
463
- options.sessionDir !== undefined
464
- ? joinPath(options.sessionDir, `${options.sessionId}.json`)
465
- : `${options.sessionId}.json`;
463
+ // Task 0086 R15: sessionId lands in a filesystem path — validate it
464
+ // before it can escape sessionDir via separators or traversal.
465
+ const id = assertSafeSessionId(options.sessionId);
466
+ const file = options.sessionDir !== undefined ? joinPath(options.sessionDir, `${id}.json`) : `${id}.json`;
466
467
  args.push('--resume', file, '--save-transcript', file);
467
468
  } else if (options.sessionDir !== undefined) {
468
469
  args.push('--save-transcript', joinPath(options.sessionDir, 'fm-session.json'));
469
470
  }
470
- args.push(options.input ?? '');
471
+ // Task 0086 R4: `--` pins the input as a positional, so dash-leading
472
+ // prompts (--help, -n) are never reparsed as fm flags.
473
+ args.push('--', options.input ?? '');
471
474
  return { command: 'fm', args };
472
475
  },
473
476
  getAuthCommand: () => ({ command: 'fm', args: ['available', '--model', 'system'] }),
@@ -83,15 +83,23 @@ function resolveApiKey(options: DecisionMakerOptions): string {
83
83
 
84
84
  const LAYA_DRIVER_PACKAGE = '@gobing-ai/ts-laya-mlx';
85
85
 
86
- async function resolveLayaDriver(options: DecisionMakerOptions): Promise<DecisionDriver> {
86
+ /**
87
+ * @internal Exported for task 0086 R9 branch tests: `importModule` lets tests
88
+ * simulate package-import failures that cannot occur in-workspace (workspace
89
+ * links always resolve).
90
+ */
91
+ export async function resolveLayaDriver(
92
+ options: DecisionMakerOptions,
93
+ importModule: (specifier: string) => Promise<unknown> = (specifier) => import(specifier),
94
+ ): Promise<DecisionDriver> {
95
+ // Task 0086 R9: only the IMPORT is translated to the install-hint error;
96
+ // construction errors from the factory itself propagate unchanged so the
97
+ // caller sees the real cause instead of a misleading install message.
98
+ let mod: { createLayaDriver?: (opts?: unknown) => DecisionDriver };
87
99
  try {
88
- const mod = (await import(LAYA_DRIVER_PACKAGE)) as {
100
+ mod = (await importModule(LAYA_DRIVER_PACKAGE)) as {
89
101
  createLayaDriver?: (opts?: unknown) => DecisionDriver;
90
102
  };
91
- if (typeof mod.createLayaDriver !== 'function') {
92
- throw new Error(`Module '${LAYA_DRIVER_PACKAGE}' does not export createLayaDriver`);
93
- }
94
- return mod.createLayaDriver(options);
95
103
  } catch (cause) {
96
104
  throw new DecisionConfigError(
97
105
  `The 'laya-local' backend requires '${LAYA_DRIVER_PACKAGE}' to be installed; install it with 'bun add ${LAYA_DRIVER_PACKAGE}'`,
@@ -99,19 +107,28 @@ async function resolveLayaDriver(options: DecisionMakerOptions): Promise<Decisio
99
107
  { cause },
100
108
  );
101
109
  }
110
+ if (typeof mod.createLayaDriver !== 'function') {
111
+ throw new DecisionConfigError(
112
+ `Module '${LAYA_DRIVER_PACKAGE}' does not export createLayaDriver`,
113
+ 'LAYA_BACKEND',
114
+ );
115
+ }
116
+ return mod.createLayaDriver(options);
102
117
  }
103
118
 
104
119
  const FM_DRIVER_PACKAGE = '@gobing-ai/ts-decision-fm';
105
120
 
106
- async function resolveFmDriver(options: DecisionMakerOptions): Promise<DecisionDriver> {
121
+ /** @internal Exported for task 0086 R9 branch tests; see {@link resolveLayaDriver}. */
122
+ export async function resolveFmDriver(
123
+ options: DecisionMakerOptions,
124
+ importModule: (specifier: string) => Promise<unknown> = (specifier) => import(specifier),
125
+ ): Promise<DecisionDriver> {
126
+ // Task 0086 R9: same import/construction split as laya above.
127
+ let mod: { createFmDriver?: (opts?: unknown) => DecisionDriver };
107
128
  try {
108
- const mod = (await import(FM_DRIVER_PACKAGE)) as {
129
+ mod = (await importModule(FM_DRIVER_PACKAGE)) as {
109
130
  createFmDriver?: (opts?: unknown) => DecisionDriver;
110
131
  };
111
- if (typeof mod.createFmDriver !== 'function') {
112
- throw new Error(`Module '${FM_DRIVER_PACKAGE}' does not export createFmDriver`);
113
- }
114
- return mod.createFmDriver(options);
115
132
  } catch (cause) {
116
133
  throw new DecisionConfigError(
117
134
  `The 'fm-local' backend requires '${FM_DRIVER_PACKAGE}' to be installed; install it with 'bun add ${FM_DRIVER_PACKAGE}'`,
@@ -119,6 +136,10 @@ async function resolveFmDriver(options: DecisionMakerOptions): Promise<DecisionD
119
136
  { cause },
120
137
  );
121
138
  }
139
+ if (typeof mod.createFmDriver !== 'function') {
140
+ throw new DecisionConfigError(`Module '${FM_DRIVER_PACKAGE}' does not export createFmDriver`, 'FM_BACKEND');
141
+ }
142
+ return mod.createFmDriver(options);
122
143
  }
123
144
 
124
145
  /**
package/src/identity.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {
2
- BunSyncProcessExecutor,
2
+ NodeSyncProcessExecutor,
3
3
  nodeBunFactory,
4
4
  type ProcessExecutor,
5
5
  type SyncProcessExecutor,
@@ -95,11 +95,12 @@ export async function getGitContext(
95
95
  * Query git for the current branch name and dirty file count synchronously.
96
96
  *
97
97
  * @deprecated Use the async {@link getGitContext} instead. Kept for one release for sync callers.
98
- * Defaults to the deprecated {@link BunSyncProcessExecutor}; new code must not rely on it.
98
+ * Defaults to {@link NodeSyncProcessExecutor} (task 0087 R7 migrated the default off the
99
+ * deprecated BunSyncProcessExecutor).
99
100
  */
100
101
  export function getGitContextSync(
101
102
  workspacePath: string,
102
- executor: SyncProcessExecutor = new BunSyncProcessExecutor(),
103
+ executor: SyncProcessExecutor = new NodeSyncProcessExecutor(),
103
104
  ): string | null {
104
105
  const branch = runGitSync(executor, ['-C', workspacePath, 'branch', '--show-current']);
105
106
  if (branch === null || branch === '') return null;