odai-dsh-agent 0.2.0 → 0.2.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/README.md +8 -4
- package/package.json +1 -1
- package/preset/odai/runtime/context-activation.mjs +71 -0
- package/preset/odai/runtime/context-capability.mjs +86 -0
- package/preset/odai/runtime/governance.mjs +8 -1
- package/preset/odai/runtime/human-care.mjs +45 -0
- package/preset/odai/runtime/human-safety-continuity.mjs +2 -2
- package/preset/odai/runtime/human-safety.mjs +3 -3
- package/preset/odai/runtime/index.mjs +336 -50
- package/preset/odai/runtime/responsibility-gap.mjs +8 -10
- package/preset/odai/runtime/responsibility-scope.mjs +166 -0
- package/preset/odai/runtime/role-overlays.mjs +4 -4
- package/preset/odai/runtime/routing-config.mjs +1 -1
- package/preset/odai/runtime/semantic-memory.mjs +2 -2
- package/preset/odai/skills/odai/SKILL.md +69 -68
- package/preset/odai/skills/odai/manifest.json +2 -1
- package/preset/odai/skills/odai/references/care.md +26 -0
- package/preset/odai/skills/odai/references/human-safety.md +33 -54
- package/src/dsh-version.mjs +6 -2
package/README.md
CHANGED
|
@@ -13,11 +13,15 @@ $DSH_HOME/.agent-presets/odai/
|
|
|
13
13
|
.odai-agent.json
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
The source composition preserves every capability row and setting from the `standard` agent surface in `@deepseek-ai/dsh@0.1.0-rc.7`, then adds the scoped Odai runtime in default `auto` mode. For rc.6, the installer deterministically restores that release's two disabled optional-provider fields before hashing and publishing the managed preset. Release verification compares each rendered composition against its exact DSH Standard source; a missing, added, reordered, or changed Standard row fails the probe. Host-owned persistence, sandbox, approval, registries, and base controller selection remain in the selected DSH profile. Ordinary requests stay on that controller. Responsibility words are optional signals rather than commands: evidence-grounded task-state gaps route at any step, identical gaps are consumed once, and an identical planner/controller model remains inline without another model call. An original request that already authorizes implementation automatically continues from a frozen planner card to executor; plan-only, new-task, expanded-scope, and unknown authorization cannot do so. Reviewer children require a current hash-addressed evidence packet whose diff and latest successful test are newer than the last write. Every route is resolved before provider I/O; deterministic invalid persisted mappings are backed up and exact-match removed, while recoverable provider failures preserve them. Frontend route failure becomes an explicit local controller fallback without a routed receipt. `execute` remains an explicit comparison mode and `observe` changes no model or child while retaining fail-closed high-impact protection. Configured compaction streams are buffered so partial failed output cannot contaminate the inherited-route retry or replace original history.
|
|
16
|
+
The source composition preserves every capability row and setting from the `standard` agent surface in `@deepseek-ai/dsh@0.1.0-rc.7`, then adds the scoped Odai runtime in default `auto` mode. For rc.6, the installer deterministically restores that release's two disabled optional-provider fields before hashing and publishing the managed preset. Release verification compares each rendered composition against its exact DSH Standard source; a missing, added, reordered, or changed Standard row fails the probe. Host-owned persistence, sandbox, approval, registries, and base controller selection remain in the selected DSH profile. Ordinary requests stay on that controller. Responsibility words are optional signals rather than commands: evidence-grounded task-state gaps route at any step, identical gaps are consumed once, and an identical planner/controller model remains inline without another model call. An original request that already authorizes implementation automatically continues from a frozen planner card to executor; plan-only, new-task, expanded-scope, and unknown authorization cannot do so. Reviewer children require a current hash-addressed evidence packet whose diff and latest successful test are newer than the last write. An incomplete packet keeps the current controller route so it can gather project-available evidence without claiming independent acceptance or terminating the authorized task. Every route is resolved before provider I/O; deterministic invalid persisted mappings are backed up and exact-match removed, while recoverable provider failures preserve them. Frontend route failure becomes an explicit local controller fallback without a routed receipt. `execute` remains an explicit comparison mode and `observe` changes no model or child while retaining fail-closed high-impact protection. Configured compaction streams are buffered so partial failed output cannot contaminate the inherited-route retry or replace original history.
|
|
17
17
|
|
|
18
|
-
The preset includes the same local long-term semantic memory runtime as the Plugin. Default `auto` mode performs no hidden model call: it automatically captures only high-confidence durable statements from the direct-human message authenticated by the latest open-turn session event, keeps ambiguous or conflicting candidates inert, and recalls only bounded active global/project records as untrusted historical context.
|
|
18
|
+
The preset includes the same local long-term semantic memory runtime as the Plugin. Default `auto` mode performs no hidden model call: it automatically captures only high-confidence durable statements from the direct-human message authenticated by the latest open-turn session event, keeps ambiguous or conflicting candidates inert, and recalls only bounded active global/project records as untrusted historical context. Matching direct intent, or the compact capability gateway on a later step, exposes the controller-only `odai_memory` tool for inspection, search, confirmation, correction, physical forgetting, exact-phrase clearing, and `auto`/`off` mode changes. Recognized secrets, contact identifiers, health/crisis content, temporary instructions, hypotheses, quotes, and code are rejected; the current user and project authority always override memory. State lives at `$DSH_HOME/odai/memory/store.json`, outside `.agent-presets`, so install, update, and uninstall preserve it. An invalid or symlinked store fails closed. The matcher is not a replacement for protecting the underlying DSH session history from sensitive input.
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Non-crisis care and crisis safety are separate. `references/care.md` owns fatigue, anxiety, self-doubt, rumination, shame, fear of mistakes, negativity, reduced agency, and user-controlled 阿岱/欧黛 styles without diagnosis, scoring, persistence, or model routing. `references/human-safety.md` owns sustained or worsening low mood, hopelessness, burden, self-harm, suicide, and immediate danger; any credible current inclination triggers timely care and a direct safety check even without a plan, while plan, means, and action determine urgency.
|
|
21
|
+
|
|
22
|
+
Human-safety continuity is an independent explicit-consent store at `$DSH_HOME/odai/human-safety-continuity.json`, not a hidden health profile and not semantic memory. `odai_human_safety_continuity` accepts only the authenticated current direct user's request to save, inspect, export, correct, remove, or physically clear user-authored care preferences, signals they want noticed, support they say helps, and safety-plan steps. Added or replacement text must occur exactly in that message; credentials and contact details are rejected, and entries persist until the user removes or physically clears them. New controller sessions receive the minimal historical record only when the current conversation independently makes care, crisis support, or record management relevant, never as proof of current risk, diagnosis, or a score; children cannot inspect it or receive its prompt data.
|
|
23
|
+
|
|
24
|
+
Ordinary requests retain the complete canonical governance plus compact responsibility and capability-discovery tools. Low-frequency control and care schemas are exposed per agent only for matching direct intent; uncommon wording can request the specialized capability on the next step, and unsupported hosts fall back to the complete tool catalog instead of dropping behavior.
|
|
21
25
|
|
|
22
26
|
## Install
|
|
23
27
|
|
|
@@ -55,7 +59,7 @@ The Agent ships no researcher, planner, executor, reviewer, or frontend model ma
|
|
|
55
59
|
前端制作用 provider-f/model-frontend,输出上限 4096。
|
|
56
60
|
```
|
|
57
61
|
|
|
58
|
-
The controller calls `odai_routing_config` to persist that explicit choice. Researcher activation is task-gated but not price-aware: its mapping enables the narrow trigger and does not guarantee lower cost. The tool repeats that warning whenever a researcher mapping is shown; Odai must use authoritative provider prices and measured usage instead of inventing either. The user does not edit Agent files, YAML, or JSON and does not add trigger terms to later tasks. Mappings live in `$DSH_HOME/odai/routing.json`, outside the managed preset, so installer updates do not report them as drift. Audit evidence likewise lives under `$DSH_HOME/odai/session-evidence/` instead of using private DSH session-event types, so changing or removing the preset cannot make a session unreadable. Changes apply from the next user turn.
|
|
62
|
+
The controller calls `odai_routing_config` to persist that explicit choice. Researcher activation is task-gated but not price-aware: its mapping enables the narrow trigger and does not guarantee lower cost. The tool repeats that warning whenever a researcher mapping is shown; Odai must use authoritative provider prices and measured usage instead of inventing either. The user does not edit Agent files, YAML, or JSON and does not add trigger terms to later tasks. Mappings live in `$DSH_HOME/odai/routing.json`, outside the managed preset, so installer updates do not report them as drift. Audit evidence likewise lives under `$DSH_HOME/odai/session-evidence/` instead of using private DSH session-event types, so changing or removing the preset cannot make a session unreadable. Changes apply from the next user turn. Whenever routing or explicit mapping management needs it, runtime resolves a fresh merged effective-mapping snapshot, with persisted mappings preferred over deployment mappings; the full snapshot enters the model prompt only for mapping-management intent and remains authoritative over stale compaction text. The tool also exposes the latest current-session actual route receipt; configured targets alone never prove that a responsibility ran. If reasoning effort is omitted, the target provider/model uses its own default rather than inheriting the source controller's setting. Plugin and Agent read the same stores when both are deliberately present.
|
|
59
63
|
|
|
60
64
|
Researcher and frontend are optional evidence/production upgrades whose missing mappings keep the original route without claiming success. A researcher child is limited to a bounded multi-source repository question; technical facts available through controller tools are investigated directly instead of manufacturing a role call. Planner, executor, and reviewer are independent optional responsibilities; if a needed one has no mapping, high-impact work fails closed and remains read-only, while lower-impact work continues only where it does not depend on the missing responsibility. Same-turn routes and child outputs require actual request-header evidence. Deterministic invalid persisted mappings are backed up and exact-match removed; credentials, quota, rate-limit, server, timeout, and transport failures preserve configuration and fall back only for the current call. A generic subagent is not a responsibility route. A manual responsibility child must use an `odai-<responsibility>` label and cannot finish successfully without a matching actual-route receipt. Odai never chooses a model or price on the user's behalf.
|
|
61
65
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export const ODAI_CONTEXTUAL_TOOL_NAMES = Object.freeze([
|
|
2
|
+
"odai_routing_config",
|
|
3
|
+
"odai_human_care",
|
|
4
|
+
"odai_human_safety",
|
|
5
|
+
"odai_route_card",
|
|
6
|
+
"odai_skill_source_config",
|
|
7
|
+
"odai_skill_evolution",
|
|
8
|
+
"odai_output_config",
|
|
9
|
+
"odai_compaction_config",
|
|
10
|
+
"odai_memory",
|
|
11
|
+
"odai_human_safety_continuity",
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
export const ODAI_CORE_TOOL_NAMES = Object.freeze(["odai_context_capability", "odai_responsibility_gap"]);
|
|
15
|
+
|
|
16
|
+
const CARE_CUE = /(?:疲惫|心累|焦虑|紧张|自我怀疑|怀疑自己|否定自己|内耗|反刍|反复纠结|羞耻|丢脸|害怕犯错|怕犯错|持续消极|提不起劲|失去行动感|阿岱|欧黛|fatigue|burn(?:ed|t) out|anxi(?:ous|ety)|self[- ]doubt|doubt myself|ruminat(?:e|ing|ion)|shame|fear of mistakes?|persistent negativity|cannot get started|companionship style|practical support style)/iu;
|
|
17
|
+
const CRISIS_CUE = /(?:持续低落|越来越低落|绝望|无望|撑不住|活不下去|不想活|想死|结束生命|成为负担|自伤|自残|轻生|自杀|伤害自己|immediate danger|persistent low mood|hopeless|cannot go on|burden|self[- ]harm|suicid(?:e|al)|want to die|kill myself|end my life)/iu;
|
|
18
|
+
const ROUTING_CONFIG_CUE = /(?:(?:研究|调查|规划|计划|执行|审查|验收|前端|researcher|planner|executor|reviewer|frontend).{0,24}(?:模型|路由|映射|provider|model|route|mapping)|(?:模型|路由|映射|provider|model|route|mapping).{0,24}(?:研究|调查|规划|计划|执行|审查|验收|前端|researcher|planner|executor|reviewer|frontend)|(?:所有|当前|职责|责任)?(?:模型|路由)(?:配置|映射)|routing config)/iu;
|
|
19
|
+
const SKILL_SOURCE_CUE = /(?:(?:odai|阿岱|欧黛).{0,20}(?:skill|治理).{0,20}(?:来源|source|bundled|auto|user)|(?:skill|治理).{0,20}(?:来源|source).{0,20}(?:odai|阿岱|欧黛))/iu;
|
|
20
|
+
const SKILL_EVOLUTION_CUE = /(?:(?:odai|skill|治理).{0,24}(?:演化|代际|generation|evolution|rebase|rollback|activate|deactivate)|(?:演化|代际|generation|evolution|rebase|rollback).{0,24}(?:odai|skill|治理))/iu;
|
|
21
|
+
const OUTPUT_CONFIG_CUE = /(?:(?:输出|回复|回答).{0,20}(?:normal|正常|简洁|精简|concise|economy|经济|token|上限)|(?:maxTokens|token 上限|output mode|输出模式))/iu;
|
|
22
|
+
const COMPACTION_CONFIG_CUE = /(?:(?:压缩|compaction).{0,32}(?:模型|provider|model|推理|reasoning|配置|设置|查看|重置)|(?:模型|provider|model).{0,20}(?:压缩摘要|compaction summary))/iu;
|
|
23
|
+
const MEMORY_CUE = /(?:记住|记忆|忘记|以后默认|今后默认|从现在起|所有项目|每个项目|长期使用|一直使用|偏好|remember|memory|forget|from now on|going forward|by default|all projects|always use|never use)/iu;
|
|
24
|
+
const CONTINUITY_CUE = /(?:(?:跨会话|安全|关怀|照护|支持).{0,24}(?:档案|记录|连续性|记住|保存|查看|显示|导出|更正|删除|清空)|(?:continuity|safety|care|support).{0,24}(?:record|remember|save|show|export|correct|remove|clear))/iu;
|
|
25
|
+
|
|
26
|
+
export function classifyContextActivation(text) {
|
|
27
|
+
const value = typeof text === "string" ? text : "";
|
|
28
|
+
const safety = CRISIS_CUE.test(value);
|
|
29
|
+
return Object.freeze({
|
|
30
|
+
care: !safety && CARE_CUE.test(value),
|
|
31
|
+
safety,
|
|
32
|
+
routingConfig: ROUTING_CONFIG_CUE.test(value),
|
|
33
|
+
skillSource: SKILL_SOURCE_CUE.test(value),
|
|
34
|
+
skillEvolution: SKILL_EVOLUTION_CUE.test(value),
|
|
35
|
+
outputConfig: OUTPUT_CONFIG_CUE.test(value),
|
|
36
|
+
compactionConfig: COMPACTION_CONFIG_CUE.test(value),
|
|
37
|
+
memory: MEMORY_CUE.test(value),
|
|
38
|
+
continuity: CONTINUITY_CUE.test(value),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function activeOdaiToolNames(activation, options = {}) {
|
|
43
|
+
if (options.child) return Object.freeze([]);
|
|
44
|
+
const names = new Set(ODAI_CORE_TOOL_NAMES);
|
|
45
|
+
if (activation.routingConfig) names.add("odai_routing_config");
|
|
46
|
+
if (activation.care) names.add("odai_human_care");
|
|
47
|
+
if (activation.safety) names.add("odai_human_safety");
|
|
48
|
+
if (options.routeCard) names.add("odai_route_card");
|
|
49
|
+
if (activation.skillSource) names.add("odai_skill_source_config");
|
|
50
|
+
if (activation.skillEvolution) names.add("odai_skill_evolution");
|
|
51
|
+
if (activation.outputConfig) names.add("odai_output_config");
|
|
52
|
+
if (activation.compactionConfig) names.add("odai_compaction_config");
|
|
53
|
+
if (activation.memory) names.add("odai_memory");
|
|
54
|
+
if (activation.continuity) names.add("odai_human_safety_continuity");
|
|
55
|
+
return Object.freeze([...names]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function inactiveOdaiToolNames(activeNames) {
|
|
59
|
+
const active = new Set(activeNames);
|
|
60
|
+
return Object.freeze(ODAI_CONTEXTUAL_TOOL_NAMES.filter((name) => !active.has(name)));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function estimateContextTokens(value) {
|
|
64
|
+
return Math.ceil(String(value ?? "").length / 4);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function estimateToolSchemaTokens(tools) {
|
|
68
|
+
if (!Array.isArray(tools) || tools.length === 0) return 0;
|
|
69
|
+
const schemas = tools.map(({ name, description, parameters }) => ({ name, description, parameters }));
|
|
70
|
+
return Math.ceil(JSON.stringify(schemas).length / 4) + 4;
|
|
71
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export const ODAI_CONTEXT_CAPABILITIES = Object.freeze([
|
|
2
|
+
"routing-config",
|
|
3
|
+
"human-care",
|
|
4
|
+
"human-safety",
|
|
5
|
+
"skill-source",
|
|
6
|
+
"skill-evolution",
|
|
7
|
+
"output-config",
|
|
8
|
+
"compaction-config",
|
|
9
|
+
"memory",
|
|
10
|
+
"safety-continuity",
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
const CAPABILITY_FIELDS = Object.freeze({
|
|
14
|
+
"routing-config": "routingConfig",
|
|
15
|
+
"human-care": "care",
|
|
16
|
+
"human-safety": "safety",
|
|
17
|
+
"skill-source": "skillSource",
|
|
18
|
+
"skill-evolution": "skillEvolution",
|
|
19
|
+
"output-config": "outputConfig",
|
|
20
|
+
"compaction-config": "compactionConfig",
|
|
21
|
+
memory: "memory",
|
|
22
|
+
"safety-continuity": "continuity",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export function requestedContextCapabilities(events, turn) {
|
|
26
|
+
if (!Number.isSafeInteger(turn)) return Object.freeze([]);
|
|
27
|
+
const capabilities = new Set();
|
|
28
|
+
for (const event of Array.isArray(events) ? events : []) {
|
|
29
|
+
if (event?.type === "odai/context-capability-requested"
|
|
30
|
+
&& event.data?.turn === turn
|
|
31
|
+
&& ODAI_CONTEXT_CAPABILITIES.includes(event.data.capability)) {
|
|
32
|
+
capabilities.add(event.data.capability);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return Object.freeze([...capabilities]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function activateRequestedCapabilities(activation, capabilities) {
|
|
39
|
+
const merged = { ...activation };
|
|
40
|
+
for (const capability of capabilities) {
|
|
41
|
+
const field = CAPABILITY_FIELDS[capability];
|
|
42
|
+
if (field) merged[field] = true;
|
|
43
|
+
}
|
|
44
|
+
if (merged.safety) merged.care = false;
|
|
45
|
+
return Object.freeze(merged);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function createContextCapabilityTool(options = {}) {
|
|
49
|
+
const isChild = typeof options.isChild === "function" ? options.isChild : () => false;
|
|
50
|
+
const onRequested = typeof options.onRequested === "function" ? options.onRequested : () => {};
|
|
51
|
+
return {
|
|
52
|
+
name: "odai_context_capability",
|
|
53
|
+
description: "Request one Odai capability when its specialized tool is not currently visible. Use this only as a discovery fallback for routing configuration, non-crisis care, crisis safety, skill source/evolution, output, compaction, memory, or safety continuity. The real tool and its complete constraints appear on the next step; this request performs no configuration, persistence, diagnosis, or model switch.",
|
|
54
|
+
parameters: {
|
|
55
|
+
type: "object",
|
|
56
|
+
additionalProperties: false,
|
|
57
|
+
required: ["capability"],
|
|
58
|
+
properties: {
|
|
59
|
+
capability: { type: "string", enum: ODAI_CONTEXT_CAPABILITIES },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
output: {
|
|
63
|
+
schema: {
|
|
64
|
+
type: "object",
|
|
65
|
+
additionalProperties: false,
|
|
66
|
+
required: ["capability", "status"],
|
|
67
|
+
properties: {
|
|
68
|
+
capability: { type: "string", enum: ODAI_CONTEXT_CAPABILITIES },
|
|
69
|
+
status: { type: "string", enum: ["available-next-step"] },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
render(_args, value) {
|
|
73
|
+
return [{ type: "text", text: `Odai ${value.capability} capability is available on the next step.` }];
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
execute(args, execution) {
|
|
77
|
+
if (!execution.agent) throw new Error("odai_context_capability requires an owning agent session");
|
|
78
|
+
if (isChild(execution.agent)) throw new Error("child agents may not request Odai controller capabilities");
|
|
79
|
+
if (!args || typeof args !== "object" || Array.isArray(args) || !ODAI_CONTEXT_CAPABILITIES.includes(args.capability)) {
|
|
80
|
+
throw new TypeError(`capability must be ${ODAI_CONTEXT_CAPABILITIES.join(", ")}`);
|
|
81
|
+
}
|
|
82
|
+
onRequested(execution.agent, args.capability, execution);
|
|
83
|
+
return Promise.resolve({ capability: args.capability, status: "available-next-step" });
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -73,10 +73,17 @@ export function activeRouteProtection(agent, recordedEvents = agent?.session?.ev
|
|
|
73
73
|
}
|
|
74
74
|
if (currentTurn === undefined) return undefined;
|
|
75
75
|
|
|
76
|
+
const releasedScopes = new Set();
|
|
76
77
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
77
78
|
const event = events[index];
|
|
79
|
+
if (event?.type === "odai/route-protection-released" && event.data?.turn === currentTurn) {
|
|
80
|
+
if (typeof event.data?.scopeId === "string") releasedScopes.add(event.data.scopeId);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
78
83
|
if (event?.type !== "odai/route-protection") continue;
|
|
79
|
-
if (event.data?.turn
|
|
84
|
+
if (event.data?.turn !== currentTurn || event.data?.mode !== "read-only") continue;
|
|
85
|
+
if (event.data?.scopeId && releasedScopes.has(event.data.scopeId)) continue;
|
|
86
|
+
return event.data;
|
|
80
87
|
}
|
|
81
88
|
return undefined;
|
|
82
89
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export const HUMAN_CARE_REFERENCE_PATH = "references/care.md";
|
|
2
|
+
|
|
3
|
+
export function createHumanCareTool(options = {}) {
|
|
4
|
+
if (typeof options.contractFor !== "function") throw new TypeError("createHumanCareTool requires contractFor");
|
|
5
|
+
return {
|
|
6
|
+
name: "odai_human_care",
|
|
7
|
+
description: "Load Odai's non-crisis care and user-controlled interaction-style contract when the current message shows fatigue, anxiety, self-doubt, rumination, shame, fear of mistakes, negativity, or reduced agency. This does not diagnose, score, persist state, or change model routing.",
|
|
8
|
+
parameters: {
|
|
9
|
+
type: "object",
|
|
10
|
+
additionalProperties: false,
|
|
11
|
+
properties: {},
|
|
12
|
+
},
|
|
13
|
+
output: {
|
|
14
|
+
schema: {
|
|
15
|
+
type: "object",
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
required: ["scope", "userChannelOwner", "contract"],
|
|
18
|
+
properties: {
|
|
19
|
+
scope: { type: "string", enum: ["non-crisis-care"] },
|
|
20
|
+
userChannelOwner: { type: "string", enum: ["current-controller", "controller"] },
|
|
21
|
+
contract: { type: "string" },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
render(_args, value) {
|
|
25
|
+
return [{
|
|
26
|
+
type: "text",
|
|
27
|
+
text: `Non-crisis care contract loaded; the ${value.userChannelOwner} owns the response.\n\n${value.contract}`,
|
|
28
|
+
}];
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
execute(args, execution) {
|
|
32
|
+
if (args === null || typeof args !== "object" || Array.isArray(args) || Object.keys(args).length > 0) {
|
|
33
|
+
throw new TypeError("odai_human_care accepts no arguments");
|
|
34
|
+
}
|
|
35
|
+
if (!execution.agent) throw new Error("odai_human_care requires an owning agent session");
|
|
36
|
+
const contract = options.contractFor(execution.agent);
|
|
37
|
+
if (typeof contract !== "string" || contract.trim() === "") throw new Error("Odai human-care contract is unavailable");
|
|
38
|
+
return Promise.resolve({
|
|
39
|
+
scope: "non-crisis-care",
|
|
40
|
+
userChannelOwner: options.isChild?.(execution.agent) ? "controller" : "current-controller",
|
|
41
|
+
contract: contract.trim(),
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -16,11 +16,11 @@ const REPLACE_CUE = /(?:更正|改成|替换|更新|correct|change|replace|updat
|
|
|
16
16
|
const REMOVE_CUE = /(?:删除|移除|忘记|撤回|不要再记|刚才那条|这条|delete|remove|forget|retract|that entry)/iu;
|
|
17
17
|
const CLEAR_CUE = /(?:清空|全部删除|彻底删除|删掉.{0,12}全部|clear|delete all|erase all)/iu;
|
|
18
18
|
const SAFETY_SCOPE_CUE = /(?:安全|关怀|照护|支持|心理|心情|跨会话|档案|连续性|safety|care|support|continuity)/iu;
|
|
19
|
-
const SAFETY_CONTENT_CUE = /(
|
|
19
|
+
const SAFETY_CONTENT_CUE = /(?:危机|心理健康|情绪|心情|压力|焦虑|怀疑自己|否定自己|自我怀疑|内耗|反刍|反复纠结|消极|提不起劲|失去行动感|低落|难受|绝望|无望|心累|心烦|崩溃|撑不住|自伤|自残|轻生|自杀|伤害自己|倾听|陪伴|安抚|说教|给我空间|阿岱|欧黛|陪伴风格|行动支撑|可信任的人|紧急服务|crisis|mental health|emotion(?:al)?|overwhelm(?:ed|ing)?|distress(?:ed)?|anxi(?:ous|ety)|self[- ]doubt|doubt myself|ruminat(?:e|ing|ion)|internal conflict|persistent negativity|negative thoughts?|cannot get started|companionship style|practical support style|depress(?:ed|ion)?|hopeless|suicid(?:e|al)|self[- ]harm|hurt myself|listen(?:ing)?|check[- ]?ins?|calm me|support me|give me space|trusted person|emergency services?)/iu;
|
|
20
20
|
const ORDINARY_PROJECT_CONTENT_CUE = /(?:项目|仓库|代码|工作|截止期|构建|依赖|包管理|配置|测试|发布|部署|接口|数据库|文件|\b(?:project|repository|repo|code|work|deadlines?|build|dependencies?|package managers?|configuration|config|tests?|deployment|release|api|database|files?|pnpm|npm|yarn)\b)/iu;
|
|
21
21
|
const TECHNICAL_OPERATIONAL_INSTRUCTION_CUE = /(?:(?:必须|需要|应当|应该|要|改用|使用|运行|配置|构建|部署|发布)[^。!?\n]{0,32}(?:pnpm|npm|yarn|项目|仓库|代码|依赖|配置|测试|接口|数据库|文件)|(?:pnpm|npm|yarn)[^。!?\n]{0,12}(?:必须|必需|强制)|\b(?:must|should|need to|use|run|configure|build|deploy|release)\b[^.!?\n]{0,32}\b(?:pnpm|npm|yarn|project|repository|repo|code|dependencies?|configuration|config|tests?|api|database|files?)\b|\b(?:pnpm|npm|yarn)\b[^.!?\n]{0,12}\b(?:required|mandatory)\b)/iu;
|
|
22
22
|
const PERSONAL_CARE_ACTION_CUE = /(?:倾听|陪伴|安抚|不要说教|少问|问我|听我|给我空间|支持我|可信任的人|紧急服务|listen(?:ing)?|check[- ]?ins?|give me space|support me|stay with me|ask me|calm me|do not lecture|trusted person|emergency services?)/iu;
|
|
23
|
-
const PROJECT_PERSONAL_CARE_LINK_CUE = /(?:(?:让我|使我|令我)[^。!?\n]{0,16}(
|
|
23
|
+
const PROJECT_PERSONAL_CARE_LINK_CUE = /(?:(?:让我|使我|令我)[^。!?\n]{0,16}(?:焦虑|怀疑自己|否定自己|自我怀疑|内耗|反刍|消极|提不起劲|低落|难受|心累|心烦|崩溃|撑不住|压力|情绪)|(?:当|如果)?我[^。!?\n]{0,16}(?:焦虑|怀疑自己|否定自己|自我怀疑|内耗|反刍|消极|提不起劲|低落|难受|心累|心烦|崩溃|撑不住|压力大|情绪不好)|(?:项目|代码|工作|截止期)[^。!?\n]{0,24}(?:让我|使我|令我)[^。!?\n]{0,12}(?:焦虑|怀疑自己|否定自己|自我怀疑|内耗|反刍|消极|提不起劲|低落|难受|心累|心烦|崩溃|撑不住|压力)|\b(?:when i am|i feel|makes? me|leaves? me)[^.!?\n]{0,24}\b(?:overwhelm(?:ed)?|distress(?:ed)?|anxi(?:ous|ety)|self[- ]doubt|doubt myself|ruminat(?:e|ing)|negative|cannot get started|depress(?:ed)?|hopeless)|\b(?:code reviews?|deployment incidents?|deadlines?|work|the project)[^.!?\n]{0,24}\b(?:overwhelm(?:s|ed)? me|makes? me anxious|makes? me doubt myself|keeps? me ruminating|distresses? me))/iu;
|
|
24
24
|
const DEICTIC_CUE = /(?:刚才|那条|这条|全部|that|this|all)/iu;
|
|
25
25
|
const FORBIDDEN_PRIVATE_VALUE = /(?:password|passwd|api[_ -]?key|access[_ -]?token|secret|密码|口令|密钥|令牌|\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b|\b\+?\d[\d ()-]{8,}\d\b)/iu;
|
|
26
26
|
const MUTATION_REFUSAL_CUE = /(?:(?:不要|别|请勿|不许|禁止|无需|不用|不想|拒绝)[^,,;;::。!?\n]{0,20}(?:跨会话|记住|保存|存入|更正|改成|替换|更新)|(?:跨会话|记住|保存|存入|更正|改成|替换|更新)[^,,;;::。!?\n]{0,24}(?:取消|算了|不要(?:了|再?(?:记住|保存|存入|更正|改|替换|更新))|别再?(?:记住|保存|存入|更正|改|替换|更新))|\b(?:do not|don't|never|no need to|refuse to|without)\b[^,;:.!?\n]{0,48}\b(?:remember|save|store|persist|correct|change|replace|update)\b|\b(?:remember|save|store|persist|correct|change|replace|update)\b[^,;:.!?\n]{0,32}\b(?:cancel|never mind|(?:do not|don't)\s+(?:remember|save|store|persist|correct|change|replace|update))\b)/iu;
|
|
@@ -4,7 +4,7 @@ export function createHumanSafetyTool(options = {}) {
|
|
|
4
4
|
if (typeof options.contractFor !== "function") throw new TypeError("createHumanSafetyTool requires contractFor");
|
|
5
5
|
return {
|
|
6
6
|
name: "odai_human_safety",
|
|
7
|
-
description: "Load Odai's highest-priority
|
|
7
|
+
description: "Load Odai's highest-priority crisis-safety contract for sustained or worsening low mood, hopelessness, burden, self-harm, suicide, or immediate danger. Invoke proactively; do not diagnose, score, persist state, or change model routing.",
|
|
8
8
|
parameters: {
|
|
9
9
|
type: "object",
|
|
10
10
|
additionalProperties: false,
|
|
@@ -19,7 +19,7 @@ export function createHumanSafetyTool(options = {}) {
|
|
|
19
19
|
priority: { type: "string", enum: ["highest"] },
|
|
20
20
|
principles: {
|
|
21
21
|
type: "array",
|
|
22
|
-
items: { type: "string", enum: ["
|
|
22
|
+
items: { type: "string", enum: ["timely-intervention", "active-guidance", "no-secondary-harm"] },
|
|
23
23
|
},
|
|
24
24
|
userChannelOwner: { type: "string", enum: ["current-controller", "controller"] },
|
|
25
25
|
contract: { type: "string" },
|
|
@@ -41,7 +41,7 @@ export function createHumanSafetyTool(options = {}) {
|
|
|
41
41
|
if (typeof contract !== "string" || contract.trim() === "") throw new Error("Odai human-safety contract is unavailable");
|
|
42
42
|
return Promise.resolve({
|
|
43
43
|
priority: "highest",
|
|
44
|
-
principles: ["
|
|
44
|
+
principles: ["timely-intervention", "active-guidance", "no-secondary-harm"],
|
|
45
45
|
userChannelOwner: options.isChild?.(execution.agent) ? "controller" : "current-controller",
|
|
46
46
|
contract: contract.trim(),
|
|
47
47
|
});
|