@superdoc/sdk 2.13.0-next.9 → 2.13.1-next.1
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/agent/actions.cjs +112 -3
- package/dist/agent/actions.d.ts +19 -0
- package/dist/agent/actions.js +110 -3
- package/dist/agent/catalog.cjs +52 -8
- package/dist/agent/catalog.d.ts +54 -1
- package/dist/agent/catalog.js +55 -8
- package/dist/embedded-tools.generated.cjs +5 -5
- package/dist/embedded-tools.generated.js +5 -5
- package/dist/generated/client.d.ts +3664 -905
- package/dist/generated/contract.cjs +1493 -4
- package/dist/generated/contract.js +1494 -4
- package/dist/index.cjs +2 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/presets/core.cjs +77 -15
- package/dist/presets/core.js +79 -17
- package/dist/presets.d.ts +23 -0
- package/dist/runtime/host.cjs +24 -15
- package/dist/runtime/host.d.ts +3 -1
- package/dist/runtime/host.js +24 -15
- package/dist/runtime/sdk-version.generated.cjs +1 -1
- package/dist/runtime/sdk-version.generated.d.ts +1 -1
- package/dist/runtime/sdk-version.generated.js +1 -1
- package/dist/tools.cjs +34 -8
- package/dist/tools.d.ts +28 -3
- package/dist/tools.js +34 -8
- package/package.json +7 -7
- package/tools/catalog.json +3517 -427
- package/tools/tools-policy.json +1 -1
- package/tools/tools.anthropic.json +3371 -281
- package/tools/tools.generic.json +4089 -999
- package/tools/tools.openai.json +4072 -982
- package/tools/tools.vercel.json +4072 -982
package/dist/index.cjs
CHANGED
|
@@ -10,6 +10,7 @@ var skills = require('./skills.cjs');
|
|
|
10
10
|
var tools = require('./tools.cjs');
|
|
11
11
|
var intentDispatch_generated = require('./generated/intent-dispatch.generated.cjs');
|
|
12
12
|
var v2PresetCompat = require('./agent/v2-preset-compat.cjs');
|
|
13
|
+
var actions = require('./agent/actions.cjs');
|
|
13
14
|
var define = require('./actions/define.cjs');
|
|
14
15
|
var presets = require('./presets.cjs');
|
|
15
16
|
|
|
@@ -216,6 +217,7 @@ exports.getToolCatalog = tools.getToolCatalog;
|
|
|
216
217
|
exports.listTools = tools.listTools;
|
|
217
218
|
exports.dispatchIntentTool = intentDispatch_generated.dispatchIntentTool;
|
|
218
219
|
exports.wrapV2PresetCompat = v2PresetCompat.wrapV2PresetCompat;
|
|
220
|
+
exports.getActionDispositions = actions.getActionDispositions;
|
|
219
221
|
exports.defineAction = define.defineAction;
|
|
220
222
|
exports.DEFAULT_PRESET = presets.DEFAULT_PRESET;
|
|
221
223
|
exports.getPreset = presets.getPreset;
|
package/dist/index.d.ts
CHANGED
|
@@ -93,7 +93,8 @@ export type { AnthropicSystemPrompt, CacheStrategy, SystemPromptForProviderResul
|
|
|
93
93
|
export { dispatchIntentTool } from './generated/intent-dispatch.generated.js';
|
|
94
94
|
export { wrapV2PresetCompat } from './agent/v2-preset-compat.js';
|
|
95
95
|
export type { BoundDocApi } from './generated/client.js';
|
|
96
|
-
export type { ActionName } from './agent/actions.js';
|
|
96
|
+
export type { ActionName, ActionDisposition } from './agent/actions.js';
|
|
97
|
+
export { getActionDispositions } from './agent/actions.js';
|
|
97
98
|
export type { AgentReceipt, AgentApplyArgs, AgentVerifyArgs, AgentInspectResult } from './agent/runtime.js';
|
|
98
99
|
export type { GetSystemPromptOptions, GetToolsOptions, GetToolsResult, PresetDescriptor } from './presets.js';
|
|
99
100
|
export { createAgentToolkit } from './tools.js';
|
package/dist/index.js
CHANGED
|
@@ -196,6 +196,7 @@ export { getSkill, installSkill, listSkills } from './skills.js';
|
|
|
196
196
|
export { chooseTools, dispatchSuperDocTool, getMcpPrompt, getSystemPrompt, getSystemPromptForProvider, getToolCatalog, listTools, DEFAULT_PRESET, getPreset, listPresets, registerPreset, unregisterPreset, } from './tools.js';
|
|
197
197
|
export { dispatchIntentTool } from './generated/intent-dispatch.generated.js';
|
|
198
198
|
export { wrapV2PresetCompat } from './agent/v2-preset-compat.js';
|
|
199
|
+
export { getActionDispositions } from './agent/actions.js';
|
|
199
200
|
export { createAgentToolkit } from './tools.js';
|
|
200
201
|
// Custom-action authoring kit: `defineAction` builds an ActionSpec (steps or
|
|
201
202
|
// run tier); hand the specs to `createAgentToolkit({ actions })`.
|
package/dist/presets/core.cjs
CHANGED
|
@@ -370,8 +370,15 @@ function applyCacheMarkers(tools, provider, cacheRequested) {
|
|
|
370
370
|
// ---------------------------------------------------------------------------
|
|
371
371
|
// PresetDescriptor surface
|
|
372
372
|
// ---------------------------------------------------------------------------
|
|
373
|
+
function policyOptionsOf(options) {
|
|
374
|
+
return {
|
|
375
|
+
excludeActions: options?.excludeActions,
|
|
376
|
+
enforceChangeMode: options?.enforceChangeMode,
|
|
377
|
+
allowDirectActions: options?.allowDirectActions,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
373
380
|
async function coreGetTools(provider, options) {
|
|
374
|
-
const tools = catalog.listAgentTools(provider,
|
|
381
|
+
const tools = catalog.listAgentTools(provider, policyOptionsOf(options));
|
|
375
382
|
return applyCacheMarkers(tools, provider, options?.cache === true);
|
|
376
383
|
}
|
|
377
384
|
async function coreGetCatalog() {
|
|
@@ -389,6 +396,7 @@ async function coreGetCatalog() {
|
|
|
389
396
|
generatedAt: null,
|
|
390
397
|
toolCount: tools.length,
|
|
391
398
|
tools,
|
|
399
|
+
dispositions: { ...actions.ACTION_DISPOSITIONS },
|
|
392
400
|
};
|
|
393
401
|
}
|
|
394
402
|
/**
|
|
@@ -467,7 +475,7 @@ function stripExcludedActionLines(prompt, excludedActions) {
|
|
|
467
475
|
.join('\n');
|
|
468
476
|
}
|
|
469
477
|
async function coreGetSystemPrompt(options) {
|
|
470
|
-
const { excludedActions } = catalog.
|
|
478
|
+
const { excludedActions } = catalog.resolveActionPolicy(policyOptionsOf(options));
|
|
471
479
|
// The core preset is ACTIONS-ONLY: superdoc_execute_code is WIP and not
|
|
472
480
|
// advertised, and the prompt documents only the action surface (this exact
|
|
473
481
|
// prompt is what the eval suite validates). Code-execution guidance will be
|
|
@@ -483,6 +491,17 @@ async function coreGetMcpPrompt() {
|
|
|
483
491
|
}
|
|
484
492
|
return _mcpPromptCache;
|
|
485
493
|
}
|
|
494
|
+
/**
|
|
495
|
+
* Dispatchable-but-unadvertised tools that route around the action layer and
|
|
496
|
+
* therefore around the tracked-changes policy. Refused outright while
|
|
497
|
+
* enforcement is on; a tracked-aware agent_apply may lift this later.
|
|
498
|
+
*/
|
|
499
|
+
const ENFORCEMENT_SIDE_DOORS = new Set([
|
|
500
|
+
'agent_apply',
|
|
501
|
+
'agent_operation',
|
|
502
|
+
'superdoc_execute_code',
|
|
503
|
+
]);
|
|
504
|
+
const POLICY_OPTION_KEYS = ['excludeActions', 'enforceChangeMode', 'allowDirectActions'];
|
|
486
505
|
async function coreDispatch(documentHandle, toolName, args, invokeOptions) {
|
|
487
506
|
if (!isRecord(args)) {
|
|
488
507
|
throw new errors.SuperDocCliError(`Tool arguments for ${toolName} must be an object.`, {
|
|
@@ -497,26 +516,69 @@ async function coreDispatch(documentHandle, toolName, args, invokeOptions) {
|
|
|
497
516
|
details: { toolName },
|
|
498
517
|
});
|
|
499
518
|
}
|
|
500
|
-
// Defense-in-depth for
|
|
501
|
-
//
|
|
502
|
-
// guessed
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
519
|
+
// Defense-in-depth for narrowed surfaces: a host that narrowed the advertised
|
|
520
|
+
// tools (excludeActions / enforceChangeMode) passes the same options here so
|
|
521
|
+
// a guessed or injected call to a hidden action is refused, not run.
|
|
522
|
+
const policyOptions = invokeOptions;
|
|
523
|
+
// Unknown names are tolerated here: a preset that extends core (custom
|
|
524
|
+
// actions) forwards one merged exclusion list, and its own action names are
|
|
525
|
+
// not core's to validate. getTools/getSystemPrompt still validate.
|
|
526
|
+
const policy = catalog.resolveActionPolicy(policyOptionsOf(policyOptions), { ignoreUnknownActions: true });
|
|
527
|
+
const enforcing = policy.enforceChangeMode === 'tracked';
|
|
528
|
+
if (enforcing && ENFORCEMENT_SIDE_DOORS.has(toolName)) {
|
|
529
|
+
throw new errors.SuperDocCliError(`${toolName} bypasses the tracked-changes policy and is refused under enforceChangeMode: "tracked".`, {
|
|
508
530
|
code: 'INVALID_ARGUMENT',
|
|
509
|
-
details: { toolName,
|
|
531
|
+
details: { toolName, reason: 'enforce-change-mode' },
|
|
510
532
|
});
|
|
511
533
|
}
|
|
534
|
+
let dispatchArgs = sanitizedArgs;
|
|
535
|
+
let changeModeForced = false;
|
|
536
|
+
if (toolName === 'superdoc_perform_action' && typeof sanitizedArgs.action === 'string') {
|
|
537
|
+
const action = sanitizedArgs.action;
|
|
538
|
+
if (policy.configuredExclusions.has(action)) {
|
|
539
|
+
throw new errors.SuperDocCliError(`Action ${action} is excluded by configuration.`, {
|
|
540
|
+
code: 'INVALID_ARGUMENT',
|
|
541
|
+
details: { toolName, action, excluded: true, reason: 'excluded' },
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
if (policy.directOnlyExclusions.has(action)) {
|
|
545
|
+
throw new errors.SuperDocCliError(`Action ${action} edits the document directly and is refused under enforceChangeMode: "tracked". Pass it in allowDirectActions to permit it.`, {
|
|
546
|
+
code: 'INVALID_ARGUMENT',
|
|
547
|
+
details: { toolName, action, excluded: true, reason: 'direct-only' },
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
if (enforcing && actions.isActionName(action)) {
|
|
551
|
+
if (actions.ACTION_DISPOSITIONS[action] === 'tracked') {
|
|
552
|
+
// Omitted or explicit "direct" both upgrade: the policy decides the
|
|
553
|
+
// mode, the model only picks the verb.
|
|
554
|
+
changeModeForced = sanitizedArgs.changeMode !== 'tracked';
|
|
555
|
+
dispatchArgs = { ...sanitizedArgs, changeMode: 'tracked' };
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
// An allowlisted direct-only action runs direct by definition. A stray
|
|
559
|
+
// changeMode:"tracked" from the model would make move_range fail (it
|
|
560
|
+
// refuses tracked) and the receipt lie, so normalize it away.
|
|
561
|
+
changeModeForced = sanitizedArgs.changeMode === 'tracked';
|
|
562
|
+
const { changeMode: _requested, ...rest } = sanitizedArgs;
|
|
563
|
+
dispatchArgs = rest;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
512
567
|
let forwardOptions = invokeOptions;
|
|
513
|
-
if (
|
|
514
|
-
const { excludeActions: _ea, ...rest } =
|
|
568
|
+
if (policyOptions && POLICY_OPTION_KEYS.some((key) => key in policyOptions)) {
|
|
569
|
+
const { excludeActions: _ea, enforceChangeMode: _ecm, allowDirectActions: _ada, ...rest } = policyOptions;
|
|
515
570
|
forwardOptions = rest;
|
|
516
571
|
}
|
|
517
572
|
if (catalog.isAgentToolName(toolName)) {
|
|
518
|
-
validateAgentToolArgs(toolName,
|
|
519
|
-
|
|
573
|
+
validateAgentToolArgs(toolName, dispatchArgs);
|
|
574
|
+
const result = await dispatchAgentTool(documentHandle, toolName, dispatchArgs, forwardOptions);
|
|
575
|
+
if (enforcing && toolName === 'superdoc_perform_action' && isRecord(result)) {
|
|
576
|
+
// Receipts under enforcement state the mode that actually ran, so a host
|
|
577
|
+
// can audit "was this edit tracked?" without re-deriving the policy.
|
|
578
|
+
const changeMode = dispatchArgs.changeMode === 'tracked' ? 'tracked' : 'direct';
|
|
579
|
+
return { ...result, changeMode, changeModeForced };
|
|
580
|
+
}
|
|
581
|
+
return result;
|
|
520
582
|
}
|
|
521
583
|
throw new errors.SuperDocCliError(`Unknown tool: ${toolName}`, {
|
|
522
584
|
code: 'TOOL_DISPATCH_NOT_FOUND',
|
package/dist/presets/core.js
CHANGED
|
@@ -18,9 +18,9 @@ import path from 'node:path';
|
|
|
18
18
|
import { fileURLToPath } from 'node:url';
|
|
19
19
|
import { SuperDocCliError } from '../runtime/errors.js';
|
|
20
20
|
import { writeSdkDebugTrace } from '../runtime/debug-trace.js';
|
|
21
|
-
import { AGENT_TOOL_DEFINITIONS, isAgentToolName, listAgentTools,
|
|
21
|
+
import { AGENT_TOOL_DEFINITIONS, isAgentToolName, listAgentTools, resolveActionPolicy, PUBLIC_AGENT_TOOL_NAMES, } from '../agent/catalog.js';
|
|
22
22
|
import { agentApply, agentInspect, agentOperation, agentVerify } from '../agent/runtime.js';
|
|
23
|
-
import { superdocPerformAction } from '../agent/actions.js';
|
|
23
|
+
import { ACTION_DISPOSITIONS, isActionName, superdocPerformAction } from '../agent/actions.js';
|
|
24
24
|
import { EMBEDDED_PROMPTS } from '../embedded-prompts.generated.js';
|
|
25
25
|
// Prompts ship as <dist>/prompts/*.md. Resolve relative to the compiled
|
|
26
26
|
// module, but the module's depth differs by build: in the SDK package this
|
|
@@ -366,8 +366,15 @@ function applyCacheMarkers(tools, provider, cacheRequested) {
|
|
|
366
366
|
// ---------------------------------------------------------------------------
|
|
367
367
|
// PresetDescriptor surface
|
|
368
368
|
// ---------------------------------------------------------------------------
|
|
369
|
+
function policyOptionsOf(options) {
|
|
370
|
+
return {
|
|
371
|
+
excludeActions: options?.excludeActions,
|
|
372
|
+
enforceChangeMode: options?.enforceChangeMode,
|
|
373
|
+
allowDirectActions: options?.allowDirectActions,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
369
376
|
async function coreGetTools(provider, options) {
|
|
370
|
-
const tools = listAgentTools(provider,
|
|
377
|
+
const tools = listAgentTools(provider, policyOptionsOf(options));
|
|
371
378
|
return applyCacheMarkers(tools, provider, options?.cache === true);
|
|
372
379
|
}
|
|
373
380
|
async function coreGetCatalog() {
|
|
@@ -385,6 +392,7 @@ async function coreGetCatalog() {
|
|
|
385
392
|
generatedAt: null,
|
|
386
393
|
toolCount: tools.length,
|
|
387
394
|
tools,
|
|
395
|
+
dispositions: { ...ACTION_DISPOSITIONS },
|
|
388
396
|
};
|
|
389
397
|
}
|
|
390
398
|
/**
|
|
@@ -463,7 +471,7 @@ function stripExcludedActionLines(prompt, excludedActions) {
|
|
|
463
471
|
.join('\n');
|
|
464
472
|
}
|
|
465
473
|
async function coreGetSystemPrompt(options) {
|
|
466
|
-
const { excludedActions } =
|
|
474
|
+
const { excludedActions } = resolveActionPolicy(policyOptionsOf(options));
|
|
467
475
|
// The core preset is ACTIONS-ONLY: superdoc_execute_code is WIP and not
|
|
468
476
|
// advertised, and the prompt documents only the action surface (this exact
|
|
469
477
|
// prompt is what the eval suite validates). Code-execution guidance will be
|
|
@@ -479,6 +487,17 @@ async function coreGetMcpPrompt() {
|
|
|
479
487
|
}
|
|
480
488
|
return _mcpPromptCache;
|
|
481
489
|
}
|
|
490
|
+
/**
|
|
491
|
+
* Dispatchable-but-unadvertised tools that route around the action layer and
|
|
492
|
+
* therefore around the tracked-changes policy. Refused outright while
|
|
493
|
+
* enforcement is on; a tracked-aware agent_apply may lift this later.
|
|
494
|
+
*/
|
|
495
|
+
const ENFORCEMENT_SIDE_DOORS = new Set([
|
|
496
|
+
'agent_apply',
|
|
497
|
+
'agent_operation',
|
|
498
|
+
'superdoc_execute_code',
|
|
499
|
+
]);
|
|
500
|
+
const POLICY_OPTION_KEYS = ['excludeActions', 'enforceChangeMode', 'allowDirectActions'];
|
|
482
501
|
async function coreDispatch(documentHandle, toolName, args, invokeOptions) {
|
|
483
502
|
if (!isRecord(args)) {
|
|
484
503
|
throw new SuperDocCliError(`Tool arguments for ${toolName} must be an object.`, {
|
|
@@ -493,26 +512,69 @@ async function coreDispatch(documentHandle, toolName, args, invokeOptions) {
|
|
|
493
512
|
details: { toolName },
|
|
494
513
|
});
|
|
495
514
|
}
|
|
496
|
-
// Defense-in-depth for
|
|
497
|
-
//
|
|
498
|
-
// guessed
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
515
|
+
// Defense-in-depth for narrowed surfaces: a host that narrowed the advertised
|
|
516
|
+
// tools (excludeActions / enforceChangeMode) passes the same options here so
|
|
517
|
+
// a guessed or injected call to a hidden action is refused, not run.
|
|
518
|
+
const policyOptions = invokeOptions;
|
|
519
|
+
// Unknown names are tolerated here: a preset that extends core (custom
|
|
520
|
+
// actions) forwards one merged exclusion list, and its own action names are
|
|
521
|
+
// not core's to validate. getTools/getSystemPrompt still validate.
|
|
522
|
+
const policy = resolveActionPolicy(policyOptionsOf(policyOptions), { ignoreUnknownActions: true });
|
|
523
|
+
const enforcing = policy.enforceChangeMode === 'tracked';
|
|
524
|
+
if (enforcing && ENFORCEMENT_SIDE_DOORS.has(toolName)) {
|
|
525
|
+
throw new SuperDocCliError(`${toolName} bypasses the tracked-changes policy and is refused under enforceChangeMode: "tracked".`, {
|
|
504
526
|
code: 'INVALID_ARGUMENT',
|
|
505
|
-
details: { toolName,
|
|
527
|
+
details: { toolName, reason: 'enforce-change-mode' },
|
|
506
528
|
});
|
|
507
529
|
}
|
|
530
|
+
let dispatchArgs = sanitizedArgs;
|
|
531
|
+
let changeModeForced = false;
|
|
532
|
+
if (toolName === 'superdoc_perform_action' && typeof sanitizedArgs.action === 'string') {
|
|
533
|
+
const action = sanitizedArgs.action;
|
|
534
|
+
if (policy.configuredExclusions.has(action)) {
|
|
535
|
+
throw new SuperDocCliError(`Action ${action} is excluded by configuration.`, {
|
|
536
|
+
code: 'INVALID_ARGUMENT',
|
|
537
|
+
details: { toolName, action, excluded: true, reason: 'excluded' },
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
if (policy.directOnlyExclusions.has(action)) {
|
|
541
|
+
throw new SuperDocCliError(`Action ${action} edits the document directly and is refused under enforceChangeMode: "tracked". Pass it in allowDirectActions to permit it.`, {
|
|
542
|
+
code: 'INVALID_ARGUMENT',
|
|
543
|
+
details: { toolName, action, excluded: true, reason: 'direct-only' },
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
if (enforcing && isActionName(action)) {
|
|
547
|
+
if (ACTION_DISPOSITIONS[action] === 'tracked') {
|
|
548
|
+
// Omitted or explicit "direct" both upgrade: the policy decides the
|
|
549
|
+
// mode, the model only picks the verb.
|
|
550
|
+
changeModeForced = sanitizedArgs.changeMode !== 'tracked';
|
|
551
|
+
dispatchArgs = { ...sanitizedArgs, changeMode: 'tracked' };
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
// An allowlisted direct-only action runs direct by definition. A stray
|
|
555
|
+
// changeMode:"tracked" from the model would make move_range fail (it
|
|
556
|
+
// refuses tracked) and the receipt lie, so normalize it away.
|
|
557
|
+
changeModeForced = sanitizedArgs.changeMode === 'tracked';
|
|
558
|
+
const { changeMode: _requested, ...rest } = sanitizedArgs;
|
|
559
|
+
dispatchArgs = rest;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
508
563
|
let forwardOptions = invokeOptions;
|
|
509
|
-
if (
|
|
510
|
-
const { excludeActions: _ea, ...rest } =
|
|
564
|
+
if (policyOptions && POLICY_OPTION_KEYS.some((key) => key in policyOptions)) {
|
|
565
|
+
const { excludeActions: _ea, enforceChangeMode: _ecm, allowDirectActions: _ada, ...rest } = policyOptions;
|
|
511
566
|
forwardOptions = rest;
|
|
512
567
|
}
|
|
513
568
|
if (isAgentToolName(toolName)) {
|
|
514
|
-
validateAgentToolArgs(toolName,
|
|
515
|
-
|
|
569
|
+
validateAgentToolArgs(toolName, dispatchArgs);
|
|
570
|
+
const result = await dispatchAgentTool(documentHandle, toolName, dispatchArgs, forwardOptions);
|
|
571
|
+
if (enforcing && toolName === 'superdoc_perform_action' && isRecord(result)) {
|
|
572
|
+
// Receipts under enforcement state the mode that actually ran, so a host
|
|
573
|
+
// can audit "was this edit tracked?" without re-deriving the policy.
|
|
574
|
+
const changeMode = dispatchArgs.changeMode === 'tracked' ? 'tracked' : 'direct';
|
|
575
|
+
return { ...result, changeMode, changeModeForced };
|
|
576
|
+
}
|
|
577
|
+
return result;
|
|
516
578
|
}
|
|
517
579
|
throw new SuperDocCliError(`Unknown tool: ${toolName}`, {
|
|
518
580
|
code: 'TOOL_DISPATCH_NOT_FOUND',
|
package/dist/presets.d.ts
CHANGED
|
@@ -72,6 +72,12 @@ export type ToolCatalog = {
|
|
|
72
72
|
generatedAt: string | null;
|
|
73
73
|
toolCount: number;
|
|
74
74
|
tools: ToolCatalogEntry[];
|
|
75
|
+
/**
|
|
76
|
+
* Per-action `changeMode` disposition (core preset only): `tracked` actions
|
|
77
|
+
* honor `changeMode: "tracked"`; `direct` actions always edit directly and
|
|
78
|
+
* are hidden under `enforceChangeMode` unless allowlisted.
|
|
79
|
+
*/
|
|
80
|
+
dispositions?: Record<string, 'tracked' | 'direct'>;
|
|
75
81
|
};
|
|
76
82
|
export interface GetToolsOptions {
|
|
77
83
|
/**
|
|
@@ -87,6 +93,19 @@ export interface GetToolsOptions {
|
|
|
87
93
|
* ignore this option.
|
|
88
94
|
*/
|
|
89
95
|
excludeActions?: readonly string[];
|
|
96
|
+
/**
|
|
97
|
+
* Tracked-changes-only policy (core preset). `'tracked'` removes every
|
|
98
|
+
* direct-only action from the surface (unless allowlisted) exactly like
|
|
99
|
+
* `excludeActions`; dispatch then upgrades tracked-capable actions to
|
|
100
|
+
* `changeMode: "tracked"`. Only `'tracked'` is accepted.
|
|
101
|
+
*/
|
|
102
|
+
enforceChangeMode?: 'tracked';
|
|
103
|
+
/**
|
|
104
|
+
* Direct-only actions to keep callable under `enforceChangeMode` (`['*']` =
|
|
105
|
+
* all of them). Requires `enforceChangeMode`; unknown names throw;
|
|
106
|
+
* tracked-capable names are ignored.
|
|
107
|
+
*/
|
|
108
|
+
allowDirectActions?: readonly string[];
|
|
90
109
|
}
|
|
91
110
|
/**
|
|
92
111
|
* Options for {@link PresetDescriptor.getSystemPrompt}. Mirrors the exclusion
|
|
@@ -97,6 +116,10 @@ export interface GetToolsOptions {
|
|
|
97
116
|
export interface GetSystemPromptOptions {
|
|
98
117
|
/** Drop the per-action documentation lines for these actions (core preset). */
|
|
99
118
|
excludeActions?: readonly string[];
|
|
119
|
+
/** Same policy as {@link GetToolsOptions.enforceChangeMode}: direct-only actions leave the prompt too. */
|
|
120
|
+
enforceChangeMode?: 'tracked';
|
|
121
|
+
/** Same allowlist as {@link GetToolsOptions.allowDirectActions}. */
|
|
122
|
+
allowDirectActions?: readonly string[];
|
|
100
123
|
}
|
|
101
124
|
export interface GetToolsResult {
|
|
102
125
|
tools: unknown[];
|
package/dist/runtime/host.cjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var node_child_process = require('node:child_process');
|
|
4
|
-
var node_readline = require('node:readline');
|
|
5
4
|
var transportCommon = require('./transport-common.cjs');
|
|
6
5
|
var errors = require('./errors.cjs');
|
|
7
6
|
var documentEvidence = require('./document-evidence.cjs');
|
|
@@ -96,7 +95,8 @@ class HostTransport {
|
|
|
96
95
|
user;
|
|
97
96
|
documentRpcEnabled;
|
|
98
97
|
child = null;
|
|
99
|
-
|
|
98
|
+
stdoutBuffer = '';
|
|
99
|
+
stdoutDataListener = null;
|
|
100
100
|
pending = new Map();
|
|
101
101
|
nextRequestId = 1;
|
|
102
102
|
connecting = null;
|
|
@@ -418,14 +418,12 @@ class HostTransport {
|
|
|
418
418
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
419
419
|
});
|
|
420
420
|
this.child = child;
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
this.
|
|
426
|
-
|
|
427
|
-
this.onStdoutLine(line);
|
|
428
|
-
});
|
|
421
|
+
// JSON-RPC is framed by ASCII LF; Node 24 readline also splits on a raw
|
|
422
|
+
// U+2028 inside otherwise valid JSON string data.
|
|
423
|
+
child.stdout.setEncoding('utf8');
|
|
424
|
+
const stdoutDataListener = (chunk) => this.onStdoutChunk(chunk);
|
|
425
|
+
this.stdoutDataListener = stdoutDataListener;
|
|
426
|
+
child.stdout.on('data', stdoutDataListener);
|
|
429
427
|
child.stderr.on('data', (chunk) => {
|
|
430
428
|
if (!FORWARD_HOST_STDERR) {
|
|
431
429
|
return;
|
|
@@ -672,6 +670,18 @@ class HostTransport {
|
|
|
672
670
|
}
|
|
673
671
|
pending.resolve(record.result);
|
|
674
672
|
}
|
|
673
|
+
onStdoutChunk(chunk) {
|
|
674
|
+
this.stdoutBuffer += chunk;
|
|
675
|
+
let newlineIndex = this.stdoutBuffer.indexOf('\n');
|
|
676
|
+
while (newlineIndex !== -1) {
|
|
677
|
+
let line = this.stdoutBuffer.slice(0, newlineIndex);
|
|
678
|
+
this.stdoutBuffer = this.stdoutBuffer.slice(newlineIndex + 1);
|
|
679
|
+
if (line.endsWith('\r'))
|
|
680
|
+
line = line.slice(0, -1);
|
|
681
|
+
this.onStdoutLine(line);
|
|
682
|
+
newlineIndex = this.stdoutBuffer.indexOf('\n');
|
|
683
|
+
}
|
|
684
|
+
}
|
|
675
685
|
mapJsonRpcError(rawError) {
|
|
676
686
|
if (typeof rawError !== 'object' || rawError == null || Array.isArray(rawError)) {
|
|
677
687
|
return new errors.SuperDocCliError('Host returned an unknown JSON-RPC error.', {
|
|
@@ -730,15 +740,14 @@ class HostTransport {
|
|
|
730
740
|
this.indeterminateDiscardCloses.clear();
|
|
731
741
|
const child = this.child;
|
|
732
742
|
if (child) {
|
|
743
|
+
if (this.stdoutDataListener)
|
|
744
|
+
child.stdout.off('data', this.stdoutDataListener);
|
|
733
745
|
child.removeAllListeners();
|
|
734
746
|
child.kill('SIGKILL');
|
|
735
747
|
}
|
|
736
748
|
this.child = null;
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
this.stdoutReader.close();
|
|
740
|
-
this.stdoutReader = null;
|
|
741
|
-
}
|
|
749
|
+
this.stdoutBuffer = '';
|
|
750
|
+
this.stdoutDataListener = null;
|
|
742
751
|
const pendingEntries = Array.from(this.pending.values());
|
|
743
752
|
this.pending.clear();
|
|
744
753
|
const rejection = error ??
|
package/dist/runtime/host.d.ts
CHANGED
|
@@ -43,7 +43,8 @@ export declare class HostTransport {
|
|
|
43
43
|
private readonly user?;
|
|
44
44
|
private readonly documentRpcEnabled;
|
|
45
45
|
private child;
|
|
46
|
-
private
|
|
46
|
+
private stdoutBuffer;
|
|
47
|
+
private stdoutDataListener;
|
|
47
48
|
private readonly pending;
|
|
48
49
|
private nextRequestId;
|
|
49
50
|
private connecting;
|
|
@@ -82,6 +83,7 @@ export declare class HostTransport {
|
|
|
82
83
|
private reconcileIndeterminateDiscardCloses;
|
|
83
84
|
private sendJsonRpcRequest;
|
|
84
85
|
private onStdoutLine;
|
|
86
|
+
private onStdoutChunk;
|
|
85
87
|
private mapJsonRpcError;
|
|
86
88
|
private handleDisconnect;
|
|
87
89
|
private cleanupProcess;
|
package/dist/runtime/host.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { createInterface } from 'node:readline';
|
|
3
2
|
import { buildOperationArgv, normalizeCollaborationAuth, resolveInvocation, } from './transport-common.js';
|
|
4
3
|
import { SuperDocCliError } from './errors.js';
|
|
5
4
|
import { registerMutationRevision } from './document-evidence.js';
|
|
@@ -93,7 +92,8 @@ export class HostTransport {
|
|
|
93
92
|
user;
|
|
94
93
|
documentRpcEnabled;
|
|
95
94
|
child = null;
|
|
96
|
-
|
|
95
|
+
stdoutBuffer = '';
|
|
96
|
+
stdoutDataListener = null;
|
|
97
97
|
pending = new Map();
|
|
98
98
|
nextRequestId = 1;
|
|
99
99
|
connecting = null;
|
|
@@ -415,14 +415,12 @@ export class HostTransport {
|
|
|
415
415
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
416
416
|
});
|
|
417
417
|
this.child = child;
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
this.
|
|
423
|
-
|
|
424
|
-
this.onStdoutLine(line);
|
|
425
|
-
});
|
|
418
|
+
// JSON-RPC is framed by ASCII LF; Node 24 readline also splits on a raw
|
|
419
|
+
// U+2028 inside otherwise valid JSON string data.
|
|
420
|
+
child.stdout.setEncoding('utf8');
|
|
421
|
+
const stdoutDataListener = (chunk) => this.onStdoutChunk(chunk);
|
|
422
|
+
this.stdoutDataListener = stdoutDataListener;
|
|
423
|
+
child.stdout.on('data', stdoutDataListener);
|
|
426
424
|
child.stderr.on('data', (chunk) => {
|
|
427
425
|
if (!FORWARD_HOST_STDERR) {
|
|
428
426
|
return;
|
|
@@ -669,6 +667,18 @@ export class HostTransport {
|
|
|
669
667
|
}
|
|
670
668
|
pending.resolve(record.result);
|
|
671
669
|
}
|
|
670
|
+
onStdoutChunk(chunk) {
|
|
671
|
+
this.stdoutBuffer += chunk;
|
|
672
|
+
let newlineIndex = this.stdoutBuffer.indexOf('\n');
|
|
673
|
+
while (newlineIndex !== -1) {
|
|
674
|
+
let line = this.stdoutBuffer.slice(0, newlineIndex);
|
|
675
|
+
this.stdoutBuffer = this.stdoutBuffer.slice(newlineIndex + 1);
|
|
676
|
+
if (line.endsWith('\r'))
|
|
677
|
+
line = line.slice(0, -1);
|
|
678
|
+
this.onStdoutLine(line);
|
|
679
|
+
newlineIndex = this.stdoutBuffer.indexOf('\n');
|
|
680
|
+
}
|
|
681
|
+
}
|
|
672
682
|
mapJsonRpcError(rawError) {
|
|
673
683
|
if (typeof rawError !== 'object' || rawError == null || Array.isArray(rawError)) {
|
|
674
684
|
return new SuperDocCliError('Host returned an unknown JSON-RPC error.', {
|
|
@@ -727,15 +737,14 @@ export class HostTransport {
|
|
|
727
737
|
this.indeterminateDiscardCloses.clear();
|
|
728
738
|
const child = this.child;
|
|
729
739
|
if (child) {
|
|
740
|
+
if (this.stdoutDataListener)
|
|
741
|
+
child.stdout.off('data', this.stdoutDataListener);
|
|
730
742
|
child.removeAllListeners();
|
|
731
743
|
child.kill('SIGKILL');
|
|
732
744
|
}
|
|
733
745
|
this.child = null;
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
this.stdoutReader.close();
|
|
737
|
-
this.stdoutReader = null;
|
|
738
|
-
}
|
|
746
|
+
this.stdoutBuffer = '';
|
|
747
|
+
this.stdoutDataListener = null;
|
|
739
748
|
const pendingEntries = Array.from(this.pending.values());
|
|
740
749
|
this.pending.clear();
|
|
741
750
|
const rejection = error ??
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
// AUTO-GENERATED by scripts/embed-version.mjs — DO NOT EDIT.
|
|
4
4
|
// Source of truth: package.json. Regenerated on every SDK build so the
|
|
5
5
|
// SDK retains its own version identity when bundled into another package.
|
|
6
|
-
const SDK_VERSION = '2.13.
|
|
6
|
+
const SDK_VERSION = '2.13.1-next.1';
|
|
7
7
|
|
|
8
8
|
exports.SDK_VERSION = SDK_VERSION;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "2.13.
|
|
1
|
+
export declare const SDK_VERSION = "2.13.1-next.1";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// AUTO-GENERATED by scripts/embed-version.mjs — DO NOT EDIT.
|
|
2
2
|
// Source of truth: package.json. Regenerated on every SDK build so the
|
|
3
3
|
// SDK retains its own version identity when bundled into another package.
|
|
4
|
-
export const SDK_VERSION = '2.13.
|
|
4
|
+
export const SDK_VERSION = '2.13.1-next.1';
|
package/dist/tools.cjs
CHANGED
|
@@ -34,6 +34,8 @@ async function chooseTools(input) {
|
|
|
34
34
|
const { tools, cacheStrategy } = await preset.getTools(input.provider, {
|
|
35
35
|
cache: input.cache === true,
|
|
36
36
|
excludeActions: input.excludeActions,
|
|
37
|
+
enforceChangeMode: input.enforceChangeMode,
|
|
38
|
+
allowDirectActions: input.allowDirectActions,
|
|
37
39
|
});
|
|
38
40
|
return {
|
|
39
41
|
tools,
|
|
@@ -102,16 +104,25 @@ const CUSTOM_TOOLKIT_PRESET_ID = 'custom_superdoc_preset';
|
|
|
102
104
|
const TOOL_PROVIDERS = new Set(['openai', 'anthropic', 'vercel', 'generic']);
|
|
103
105
|
/**
|
|
104
106
|
* One-call agent surface: tools, system prompt, and a pre-bound dispatcher
|
|
105
|
-
* that are coherent BY CONSTRUCTION — the same preset
|
|
106
|
-
* apply to all three, so an
|
|
107
|
-
* being excluded from the tool
|
|
107
|
+
* that are coherent BY CONSTRUCTION — the same preset, `excludeActions`,
|
|
108
|
+
* `enforceChangeMode`, and `allowDirectActions` apply to all three, so an
|
|
109
|
+
* action can never linger in the prompt after being excluded from the tool
|
|
110
|
+
* array (or vice versa).
|
|
108
111
|
*
|
|
109
112
|
* The legacy preset ignores exclusion options everywhere (it has no action
|
|
110
113
|
* surface); passing `excludeActions` with `preset: 'legacy'` is a no-op,
|
|
111
114
|
* matching the standalone functions.
|
|
112
115
|
*/
|
|
113
116
|
async function createAgentToolkit(input) {
|
|
114
|
-
|
|
117
|
+
// Copy the lists so later caller mutation cannot desync dispatch from the
|
|
118
|
+
// tools and prompt that were generated here.
|
|
119
|
+
const policy = {
|
|
120
|
+
...(input.excludeActions ? { excludeActions: [...input.excludeActions] } : {}),
|
|
121
|
+
...(input.enforceChangeMode !== undefined ? { enforceChangeMode: input.enforceChangeMode } : {}),
|
|
122
|
+
...(input.allowDirectActions ? { allowDirectActions: [...input.allowDirectActions] } : {}),
|
|
123
|
+
};
|
|
124
|
+
const hasPolicy = Object.keys(policy).length > 0;
|
|
125
|
+
const excludeActions = policy.excludeActions;
|
|
115
126
|
// One-call custom-actions path: define your actions, hand them here as
|
|
116
127
|
// `customActions`, use the
|
|
117
128
|
// returned toolkit. We build an ephemeral extended/composed preset over
|
|
@@ -122,6 +133,16 @@ async function createAgentToolkit(input) {
|
|
|
122
133
|
// otherwise `{ base: 'core', customActions: [] }` would silently fall through to
|
|
123
134
|
// the default (legacy) preset, a different tool family than the one named.
|
|
124
135
|
if ((input.customActions && input.customActions.length > 0) || input.includeActions != null || input.base != null) {
|
|
136
|
+
// A custom action runs its own step list through runCustomAction, which
|
|
137
|
+
// never passes the core dispatch guard, so the tracked-only policy cannot
|
|
138
|
+
// cover it. Enforcing built-ins while custom actions edit directly would be
|
|
139
|
+
// a policy that silently does not hold — refuse the combination instead.
|
|
140
|
+
if (input.enforceChangeMode !== undefined) {
|
|
141
|
+
throw new errors.SuperDocCliError('enforceChangeMode is not supported together with custom actions; a custom action bypasses the tracked-changes guard.', {
|
|
142
|
+
code: 'INVALID_ARGUMENT',
|
|
143
|
+
details: { enforceChangeMode: input.enforceChangeMode, reason: 'custom-actions-unsupported' },
|
|
144
|
+
});
|
|
145
|
+
}
|
|
125
146
|
// This path builds tools directly (no chooseTools), so it must validate
|
|
126
147
|
// the provider itself; an unknown value would otherwise yield a silently
|
|
127
148
|
// mixed-dialect tool list. Mirrors the Python `create_agent_toolkit` guard.
|
|
@@ -156,12 +177,12 @@ async function createAgentToolkit(input) {
|
|
|
156
177
|
};
|
|
157
178
|
}
|
|
158
179
|
const presetId = input.preset ?? presets.DEFAULT_PRESET;
|
|
159
|
-
const { tools, meta } = await chooseTools({ ...input, preset: presetId,
|
|
160
|
-
const systemPrompt = await getSystemPrompt(presetId,
|
|
180
|
+
const { tools, meta } = await chooseTools({ ...input, preset: presetId, ...policy });
|
|
181
|
+
const systemPrompt = await getSystemPrompt(presetId, hasPolicy ? policy : undefined);
|
|
161
182
|
const dispatch = (documentHandle, toolName, args = {}, invokeOptions) => dispatchSuperDocTool(documentHandle, toolName, args, {
|
|
162
183
|
...invokeOptions,
|
|
163
184
|
preset: presetId,
|
|
164
|
-
...
|
|
185
|
+
...policy,
|
|
165
186
|
});
|
|
166
187
|
return { tools, meta, systemPrompt, dispatch };
|
|
167
188
|
}
|
|
@@ -202,7 +223,12 @@ async function getMcpPrompt(preset) {
|
|
|
202
223
|
* ```
|
|
203
224
|
*/
|
|
204
225
|
async function getSystemPromptForProvider(input) {
|
|
205
|
-
const
|
|
226
|
+
const promptOptions = {
|
|
227
|
+
...(input.excludeActions ? { excludeActions: input.excludeActions } : {}),
|
|
228
|
+
...(input.enforceChangeMode !== undefined ? { enforceChangeMode: input.enforceChangeMode } : {}),
|
|
229
|
+
...(input.allowDirectActions ? { allowDirectActions: input.allowDirectActions } : {}),
|
|
230
|
+
};
|
|
231
|
+
const text = await getSystemPrompt(input.preset, Object.keys(promptOptions).length > 0 ? promptOptions : undefined);
|
|
206
232
|
const cacheRequested = input.cache === true;
|
|
207
233
|
if (input.provider === 'anthropic') {
|
|
208
234
|
const block = { type: 'text', text };
|