dsh-agentic-pet 0.4.9 → 0.4.11
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/carrier/lib/main.js +93 -2
- package/client/pet-settings.js +308 -14
- package/lib/{service-BZbhT92u.js → driver-BiSFFYXw.js} +3530 -2059
- package/lib/index.js +433 -48
- package/lib/tools.js +1 -1
- package/lib/types/actions.d.ts +30 -0
- package/lib/types/binding.d.ts +99 -0
- package/lib/types/body.d.ts +85 -0
- package/lib/types/driver.d.ts +33 -2
- package/lib/types/electron-lock.d.ts +25 -0
- package/lib/types/electron-runtime-status.d.ts +20 -0
- package/lib/types/inner-voice.d.ts +19 -1
- package/lib/types/journal.d.ts +61 -0
- package/lib/types/presence.d.ts +27 -0
- package/lib/types/rhythm.d.ts +21 -0
- package/lib/types/service.d.ts +12 -0
- package/lib/types/settings-page.d.ts +2 -1
- package/package.json +3 -3
- package/presets/pet-inner-voice.patch.yml +2 -0
- package/presets/pet.patch.yml +1 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binding facts shared by the driver and the settings page: preset
|
|
3
|
+
* validation, attach-failure classification, path containment, and the
|
|
4
|
+
* public diagnostics shape. Pure functions only — the mutable owner map
|
|
5
|
+
* stays in the driver.
|
|
6
|
+
* @module dsh-agentic-pet/binding
|
|
7
|
+
*/
|
|
8
|
+
/** Presets that mount the pet tool row (`pet-tools`). */
|
|
9
|
+
export declare const PET_PRESET_IDS: ReadonlySet<string>;
|
|
10
|
+
export declare function isPetPreset(preset: string | undefined | null): boolean;
|
|
11
|
+
export type VoiceAttachErrorCode = 'none' | 'cwd-mismatch' | 'registry-missing' | 'session-archived' | 'session-missing' | 'preset-mismatch' | 'attach-failed';
|
|
12
|
+
export interface VoiceAttachState {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
errorCode: VoiceAttachErrorCode;
|
|
15
|
+
error: string | null;
|
|
16
|
+
expectedCwd: string;
|
|
17
|
+
actualCwd: string | null;
|
|
18
|
+
at: number;
|
|
19
|
+
}
|
|
20
|
+
export interface VoiceBindingView {
|
|
21
|
+
source: 'boundSessions' | 'innerVoiceStore' | 'none';
|
|
22
|
+
configuredSessionId: string | null;
|
|
23
|
+
expectedSessionId: string | null;
|
|
24
|
+
effectiveSessionId: string | null;
|
|
25
|
+
storeSessionId: string | null;
|
|
26
|
+
usable: boolean;
|
|
27
|
+
/** The CURRENT preset (live projection or projection cache), not the
|
|
28
|
+
* session header's creation-time value. */
|
|
29
|
+
preset: string | null;
|
|
30
|
+
presetWarning: boolean;
|
|
31
|
+
cwd: string | null;
|
|
32
|
+
lastError: string | null;
|
|
33
|
+
armed: boolean;
|
|
34
|
+
armedAt: number | null;
|
|
35
|
+
attach: VoiceAttachState | null;
|
|
36
|
+
owners: string[];
|
|
37
|
+
staleOwners: string[];
|
|
38
|
+
}
|
|
39
|
+
export interface VoiceBindingFailure {
|
|
40
|
+
at: number;
|
|
41
|
+
petId: string;
|
|
42
|
+
sessionId: string;
|
|
43
|
+
errorCode: VoiceAttachErrorCode;
|
|
44
|
+
error: string;
|
|
45
|
+
expectedCwd: string;
|
|
46
|
+
actualCwd: string | null;
|
|
47
|
+
}
|
|
48
|
+
export interface VoiceBindingSnapshot {
|
|
49
|
+
updatedAt: number;
|
|
50
|
+
pets: Record<string, VoiceBindingView>;
|
|
51
|
+
failures: VoiceBindingFailure[];
|
|
52
|
+
}
|
|
53
|
+
export interface SessionHeaderInfo {
|
|
54
|
+
id?: string;
|
|
55
|
+
agentPreset?: string;
|
|
56
|
+
cwd?: string;
|
|
57
|
+
title?: string;
|
|
58
|
+
}
|
|
59
|
+
/** Structural face of the host's live Session store (`ctx.get('sessions')`).
|
|
60
|
+
* `get()` only sees LIVE sessions; a stored-but-not-open binding is found
|
|
61
|
+
* through {@link SessionPersistenceFace} instead. */
|
|
62
|
+
export interface SessionStoreFace {
|
|
63
|
+
get(sessionId: string): {
|
|
64
|
+
header?: SessionHeaderInfo;
|
|
65
|
+
} | undefined;
|
|
66
|
+
}
|
|
67
|
+
/** Structural face of the host's session persistence listing. */
|
|
68
|
+
export interface SessionPersistenceFace {
|
|
69
|
+
list(options?: unknown): Promise<readonly {
|
|
70
|
+
header?: SessionHeaderInfo;
|
|
71
|
+
}[]>;
|
|
72
|
+
}
|
|
73
|
+
/** Structural face of the host's preset registry. `presetForSession` reads
|
|
74
|
+
* the session's committed `agent-preset/selected` projection — the current
|
|
75
|
+
* mode, unlike the immutable creation-time header field. */
|
|
76
|
+
export interface AgentPresetFace {
|
|
77
|
+
presetForSession(session: unknown): string | null;
|
|
78
|
+
}
|
|
79
|
+
/** Structural face of the zero-I/O projection cache, used to read the
|
|
80
|
+
* current preset of a stored-but-not-open session. */
|
|
81
|
+
export interface SessionProjectionCacheFace {
|
|
82
|
+
cachedSnapshot(meta: unknown): {
|
|
83
|
+
values?: {
|
|
84
|
+
agentPreset?: unknown;
|
|
85
|
+
};
|
|
86
|
+
} | undefined;
|
|
87
|
+
}
|
|
88
|
+
/** Parse the host's attachSession rejection into a stable code. The cwd is
|
|
89
|
+
* the only field worth surfacing verbatim; everything else is a category. */
|
|
90
|
+
export declare function classifyAttachFailure(message: string): {
|
|
91
|
+
errorCode: VoiceAttachErrorCode;
|
|
92
|
+
actualCwd: string | null;
|
|
93
|
+
};
|
|
94
|
+
/** Whether this settings patch actually edits the electron binding. Unrelated
|
|
95
|
+
* saves must not be hard-rejected because an EXISTING binding is invalid. */
|
|
96
|
+
export declare function patchTouchesElectronBinding(patch: Record<string, unknown>): boolean;
|
|
97
|
+
/** The host attaches a session only when its cwd IS the workspace path
|
|
98
|
+
* (canonical equality), not a descendant. */
|
|
99
|
+
export declare function isPetWorkspace(cwd: string | undefined | null, root: string): boolean;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* body/1 — the body's own description of what it is and what it can do.
|
|
3
|
+
* The driver owns this envelope (shapes, validation); the body owns the
|
|
4
|
+
* values (ids, tags, usage, fallbacks). Unknown fields and capability kinds
|
|
5
|
+
* are preserved, never interpreted here.
|
|
6
|
+
* @module dsh-agentic-pet/body
|
|
7
|
+
*/
|
|
8
|
+
export interface BodyCapability {
|
|
9
|
+
readonly supported: boolean;
|
|
10
|
+
readonly usage: string;
|
|
11
|
+
readonly example?: {
|
|
12
|
+
readonly tool: string;
|
|
13
|
+
readonly args: Readonly<Record<string, unknown>>;
|
|
14
|
+
};
|
|
15
|
+
readonly catalog?: readonly string[];
|
|
16
|
+
readonly tags?: readonly string[];
|
|
17
|
+
readonly count?: number;
|
|
18
|
+
/** catalog/tags are illustrative; count is the real total. */
|
|
19
|
+
readonly sample?: boolean;
|
|
20
|
+
readonly vocabulary?: string;
|
|
21
|
+
readonly fallback?: {
|
|
22
|
+
readonly note: string;
|
|
23
|
+
readonly substitute: {
|
|
24
|
+
readonly tool: string;
|
|
25
|
+
readonly args: Readonly<Record<string, unknown>>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
readonly [key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface BodyDescription {
|
|
31
|
+
readonly schema: 'body/1';
|
|
32
|
+
readonly bodyId?: string;
|
|
33
|
+
readonly renderer?: string;
|
|
34
|
+
readonly appearance?: {
|
|
35
|
+
readonly name: string;
|
|
36
|
+
readonly [key: string]: unknown;
|
|
37
|
+
};
|
|
38
|
+
readonly hitboxes?: {
|
|
39
|
+
readonly parts: readonly string[];
|
|
40
|
+
readonly rects?: Readonly<Record<string, readonly number[]>>;
|
|
41
|
+
readonly coordinateSpace?: string;
|
|
42
|
+
readonly [key: string]: unknown;
|
|
43
|
+
};
|
|
44
|
+
readonly capabilities?: Readonly<Record<string, BodyCapability>>;
|
|
45
|
+
readonly extensions?: Readonly<Record<string, unknown>>;
|
|
46
|
+
readonly [key: string]: unknown;
|
|
47
|
+
}
|
|
48
|
+
export type BodyNormalization = {
|
|
49
|
+
readonly kind: 'body';
|
|
50
|
+
readonly body: BodyDescription;
|
|
51
|
+
} | {
|
|
52
|
+
readonly kind: 'legacy';
|
|
53
|
+
readonly reason: string;
|
|
54
|
+
};
|
|
55
|
+
/** Validate the body/1 envelope; preserve every unknown field. A missing or
|
|
56
|
+
* foreign schema is legacy, not an error: old bodies keep working. */
|
|
57
|
+
export declare function normalizeBody(raw: unknown): BodyNormalization;
|
|
58
|
+
export type ExpressionResolution = {
|
|
59
|
+
readonly kind: 'ok';
|
|
60
|
+
readonly expression: string;
|
|
61
|
+
} | {
|
|
62
|
+
readonly kind: 'unsupported';
|
|
63
|
+
readonly capability: BodyCapability;
|
|
64
|
+
} | {
|
|
65
|
+
readonly kind: 'unknown';
|
|
66
|
+
readonly available: readonly string[];
|
|
67
|
+
} | {
|
|
68
|
+
readonly kind: 'missing';
|
|
69
|
+
};
|
|
70
|
+
/** Resolve pet_express against the CURRENT body's own expression capability.
|
|
71
|
+
* There is no cross-body expression vocabulary. */
|
|
72
|
+
export declare function resolveExpressionRequest(body: BodyDescription | null, requested: string): ExpressionResolution;
|
|
73
|
+
export interface ExpressionFallbackSuggestion {
|
|
74
|
+
readonly message: string;
|
|
75
|
+
readonly fallback: {
|
|
76
|
+
readonly tool: string;
|
|
77
|
+
readonly args: Readonly<Record<string, unknown>>;
|
|
78
|
+
} | null;
|
|
79
|
+
readonly availableTags: readonly string[];
|
|
80
|
+
}
|
|
81
|
+
/** Compute the degradation per REQUEST, not per body: a name that is also an
|
|
82
|
+
* action tag gets that tag; otherwise the body's declared default is used
|
|
83
|
+
* (and labelled as a default), never presented as a per-request match. */
|
|
84
|
+
export declare function suggestExpressionFallback(capability: BodyCapability, requested: string, actionTags: readonly string[]): ExpressionFallbackSuggestion;
|
|
85
|
+
export declare function formatMissingExpression(): string;
|
package/lib/types/driver.d.ts
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* @module dsh-agentic-pet/driver
|
|
8
8
|
*/
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
10
|
-
import type
|
|
10
|
+
import { type VoiceBindingSnapshot, type VoiceBindingView } from './binding.ts';
|
|
11
|
+
import { type PetService } from './service.ts';
|
|
11
12
|
declare module '@deepseek-ai/cordis' {
|
|
12
13
|
interface Events {
|
|
13
14
|
'api-session/status'(sessionId: string, running: boolean): void;
|
|
@@ -15,6 +16,25 @@ declare module '@deepseek-ai/cordis' {
|
|
|
15
16
|
'api-session/error'(sessionId: string, message: string): void;
|
|
16
17
|
'api-session/removed'(sessionId: string): void;
|
|
17
18
|
'pet-settings/changed'(): void;
|
|
19
|
+
'pet-binding/failed'(payload: {
|
|
20
|
+
petId: string;
|
|
21
|
+
sessionId: string;
|
|
22
|
+
errorCode: string;
|
|
23
|
+
expectedCwd: string;
|
|
24
|
+
actualCwd: string | null;
|
|
25
|
+
at: number;
|
|
26
|
+
}): void;
|
|
27
|
+
'pet-binding/armed'(payload: {
|
|
28
|
+
petId: string;
|
|
29
|
+
sessionId: string;
|
|
30
|
+
at: number;
|
|
31
|
+
}): void;
|
|
32
|
+
'pet-binding/rebound'(payload: {
|
|
33
|
+
petId: string;
|
|
34
|
+
fromSessionId: string;
|
|
35
|
+
toSessionId: string | null;
|
|
36
|
+
at: number;
|
|
37
|
+
}): void;
|
|
18
38
|
'approval/request'(request: {
|
|
19
39
|
toolName?: string;
|
|
20
40
|
reason?: string;
|
|
@@ -53,7 +73,18 @@ declare module '@deepseek-ai/cordis' {
|
|
|
53
73
|
* tool, so timestamps show the USER's wall clock, not UTC (toISOString's Z
|
|
54
74
|
* form read 8h off and cost a confusion round). Sortable, zero-padded. */
|
|
55
75
|
export declare const logStamp: () => string;
|
|
56
|
-
|
|
76
|
+
/** Export for the settings page: the live snapshot, no file round-trip. */
|
|
77
|
+
export declare function voiceBindingSnapshot(): VoiceBindingSnapshot;
|
|
78
|
+
export interface PetDriverControl {
|
|
79
|
+
bindingSnapshot(): VoiceBindingSnapshot;
|
|
80
|
+
testBinding(petId: string): Promise<{
|
|
81
|
+
ok: boolean;
|
|
82
|
+
petId: string;
|
|
83
|
+
sessionId: string | null;
|
|
84
|
+
binding: VoiceBindingView;
|
|
85
|
+
}>;
|
|
86
|
+
}
|
|
87
|
+
export declare function installPetDriver(ctx: Context, home: string, pet: PetService): PetDriverControl;
|
|
57
88
|
/**
|
|
58
89
|
* The model-expression layer: three thin tools over the pet's control
|
|
59
90
|
* interface (§6 — accurate state stays program-driven; these carry only the
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install-lock policy for the npm Electron bootstrap. The lock is a plain
|
|
3
|
+
* file; liveness of the recorded owner is the primary staleness signal, so a
|
|
4
|
+
* host restart during a download cannot block the next activation for a fixed
|
|
5
|
+
* grace period. Time-based expiry remains only as a backstop for PID reuse,
|
|
6
|
+
* malformed locks, and an owner whose event loop is stuck.
|
|
7
|
+
* @module dsh-agentic-pet/electron-lock
|
|
8
|
+
*/
|
|
9
|
+
export declare const INSTALL_LOCK_STALE_MS: number;
|
|
10
|
+
export declare const INSTALL_LOCK_MALFORMED_GRACE_MS = 60000;
|
|
11
|
+
export interface InstallLockRecord {
|
|
12
|
+
pid?: unknown;
|
|
13
|
+
at?: unknown;
|
|
14
|
+
}
|
|
15
|
+
/** True when `pid` names a live process (EPERM means it exists but is not ours). */
|
|
16
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
17
|
+
/** Parse a lock file; `undefined` means empty/malformed. */
|
|
18
|
+
export declare function parseInstallLock(raw: string): InstallLockRecord | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Whether an existing lock may be taken over by a new installer.
|
|
21
|
+
* A dead owner is stale immediately; a live owner keeps its lock until the
|
|
22
|
+
* absolute cap (which also bounds PID-reuse false positives). A malformed
|
|
23
|
+
* record gets a short grace for a concurrent writer, then goes stale.
|
|
24
|
+
*/
|
|
25
|
+
export declare function installLockStale(raw: string | undefined, mtimeMs: number, now: number, alive?: (pid: number) => boolean): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted state of the Electron runtime bootstrap, shared by the installer
|
|
3
|
+
* path (writer) and the settings page (reader). The settings page is the only
|
|
4
|
+
* user-visible surface while the first install downloads ~158 MB, so this
|
|
5
|
+
* file is deliberately plain JSON in the pet home rather than process state.
|
|
6
|
+
* @module dsh-agentic-pet/electron-runtime-status
|
|
7
|
+
*/
|
|
8
|
+
export type ElectronRuntimeState = 'preparing' | 'downloading' | 'ready' | 'failed' | 'busy' | 'unavailable';
|
|
9
|
+
export interface ElectronRuntimeStatus {
|
|
10
|
+
state: ElectronRuntimeState;
|
|
11
|
+
updatedAt: number;
|
|
12
|
+
startedAt?: number;
|
|
13
|
+
finishedAt?: number;
|
|
14
|
+
attempt?: number;
|
|
15
|
+
source?: 'npmmirror' | 'github' | 'explicit';
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function electronRuntimeStatusPath(home: string): string;
|
|
19
|
+
export declare function readElectronRuntimeStatus(home: string): ElectronRuntimeStatus | undefined;
|
|
20
|
+
export declare function writeElectronRuntimeStatus(home: string, status: ElectronRuntimeStatus): void;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* SessionController calls, and the per-pet pending locks.
|
|
7
7
|
* @module dsh-agentic-pet/inner-voice
|
|
8
8
|
*/
|
|
9
|
+
import { type PresenceFacts } from './presence.ts';
|
|
9
10
|
/** Durable petId → sessionId mapping for the inner-voice sessions. */
|
|
10
11
|
export interface InnerVoiceStore {
|
|
11
12
|
readonly sessions: Record<string, {
|
|
@@ -187,6 +188,8 @@ export interface GreetPromptFacts {
|
|
|
187
188
|
bondTier: string;
|
|
188
189
|
/** 用户起的名字(未起则内置默认):自我介绍的依据,改名后第一天尤其重要。 */
|
|
189
190
|
name: string;
|
|
191
|
+
/** 用户刚从一段离开中回来:最多一行的期间摘要。 */
|
|
192
|
+
presence?: PresenceFacts;
|
|
190
193
|
}
|
|
191
194
|
/** The boot-greeting prompt: fired once per dsh start when the pet first
|
|
192
195
|
* comes online — before the random-wake cadence takes over. Always speaks
|
|
@@ -236,10 +239,23 @@ export interface WakePromptFacts {
|
|
|
236
239
|
fromName: string;
|
|
237
240
|
count: number;
|
|
238
241
|
}>;
|
|
242
|
+
/** 静默时段:只压制世界时钟;自主唤醒仍触发,prompt 说明用户可能已睡。 */
|
|
243
|
+
quiet?: boolean;
|
|
244
|
+
/** 最近几条连续性记录(已按 maxEntryChars 截断、已做边界转义)。 */
|
|
245
|
+
journal?: Array<{
|
|
246
|
+
at: number;
|
|
247
|
+
source: string;
|
|
248
|
+
label?: string;
|
|
249
|
+
text: string;
|
|
250
|
+
}>;
|
|
251
|
+
/** 用户刚从一段离开中回来:最多一行的期间摘要。 */
|
|
252
|
+
presence?: PresenceFacts;
|
|
239
253
|
}
|
|
240
254
|
/** Local HH:mm for plan times: the model's clock talk is the USER's wall
|
|
241
255
|
* clock (logStamp precedent), and plans are compared against it. */
|
|
242
256
|
export declare const wakePlanClock: (at: number) => string;
|
|
257
|
+
/** Date + local time for journal entries (imported entries may be days old). */
|
|
258
|
+
export declare const journalClock: (at: number) => string;
|
|
243
259
|
/** The wake prompt. Time context is injected by the preset's time-context
|
|
244
260
|
* row; the leading tag doubles as the durable trigger marker in the log —
|
|
245
261
|
* and now names the SOURCE: [世界时钟] (the environment's tick), [自主唤醒]
|
|
@@ -259,6 +275,8 @@ export interface TouchPromptFacts {
|
|
|
259
275
|
affection: number;
|
|
260
276
|
/** ±60s 内到期的自主计划:合并进这次回应,计划标记为已消费。 */
|
|
261
277
|
mergedPlan?: MergedWakePlan;
|
|
278
|
+
/** 用户刚从一段离开中回来:最多一行的期间摘要。 */
|
|
279
|
+
presence?: PresenceFacts;
|
|
262
280
|
}
|
|
263
281
|
export declare function buildTouchPrompt(facts: TouchPromptFacts): string;
|
|
264
282
|
/** The chat上行 prompt (§6 2026-10-31 revision): the user typed at the
|
|
@@ -266,4 +284,4 @@ export declare function buildTouchPrompt(facts: TouchPromptFacts): string;
|
|
|
266
284
|
* (and may pet_do); it is a conversation turn, not a touch reaction. Length
|
|
267
285
|
* clamps to the contract's 2000-character ceiling. A plan inside the ±60s
|
|
268
286
|
* merge window folds in: the tag becomes the union and the intent rides. */
|
|
269
|
-
export declare function buildChatPrompt(text: string, mergedPlan?: MergedWakePlan): string;
|
|
287
|
+
export declare function buildChatPrompt(text: string, mergedPlan?: MergedWakePlan, presence?: PresenceFacts): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pet's continuity journal: one human-readable markdown file per pet.
|
|
3
|
+
* Entries carry a machine marker (timestamp/source/session) plus a human
|
|
4
|
+
* heading. Writes are append-only; the only rewrite is bounded rotation into
|
|
5
|
+
* `journal.archive.md`. The settings page's direct file edits are detected
|
|
6
|
+
* through a sidecar digest and leave an `external-edit` trace.
|
|
7
|
+
* @module dsh-agentic-pet/journal
|
|
8
|
+
*/
|
|
9
|
+
export interface JournalConfig {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
injectRecent: number;
|
|
12
|
+
maxEntryChars: number;
|
|
13
|
+
maxFileBytes: number;
|
|
14
|
+
allowUnboundAppend: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface JournalEntry {
|
|
17
|
+
at: number;
|
|
18
|
+
source: string;
|
|
19
|
+
sessionId: string | null;
|
|
20
|
+
text: string;
|
|
21
|
+
/** Hash chain: previous entry's hash ('genesis' for the first chained entry). */
|
|
22
|
+
prev?: string;
|
|
23
|
+
/** sha256(prev + canonical entry). Present on entries written by this module. */
|
|
24
|
+
hash?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface JournalInjection {
|
|
27
|
+
at: number;
|
|
28
|
+
source: string;
|
|
29
|
+
/** Human-readable source for the wake prompt (unbound writes included). */
|
|
30
|
+
label: string;
|
|
31
|
+
text: string;
|
|
32
|
+
}
|
|
33
|
+
export declare const DEFAULT_JOURNAL_CONFIG: JournalConfig;
|
|
34
|
+
export declare function journalPath(home: string, petId: string): string;
|
|
35
|
+
export declare function journalArchivePath(home: string, petId: string): string;
|
|
36
|
+
export declare function journalMetaPath(home: string, petId: string): string;
|
|
37
|
+
export declare function readJournalConfig(home: string): Promise<JournalConfig>;
|
|
38
|
+
export declare const journalSourceLabel: (source: string) => string;
|
|
39
|
+
export declare function formatJournalEntry(entry: JournalEntry): string;
|
|
40
|
+
/** Chain link for an entry body. The text is part of the hash, so editing a
|
|
41
|
+
* body without recomputing every later link is detectable. */
|
|
42
|
+
export declare function hashJournalEntry(prev: string, entry: JournalEntry): string;
|
|
43
|
+
/** Markers are authoritative; a hand-written `##` file falls back to headings.
|
|
44
|
+
* Content BEFORE the first marker is still parsed as imported entries — a
|
|
45
|
+
* user note prepended to a marker file must not live in a blind spot. */
|
|
46
|
+
export declare function parseJournalEntries(raw: string, fallbackAt?: number): JournalEntry[];
|
|
47
|
+
export declare function readJournal(home: string, petId: string, limit?: number): Promise<JournalEntry[]>;
|
|
48
|
+
export declare function readJournalForInjection(home: string, petId: string, config: JournalConfig): Promise<JournalInjection[]>;
|
|
49
|
+
export declare function appendJournal(home: string, petId: string, entry: {
|
|
50
|
+
at?: number;
|
|
51
|
+
source: string;
|
|
52
|
+
sessionId: string | null;
|
|
53
|
+
text: string;
|
|
54
|
+
}, config: JournalConfig): Promise<{
|
|
55
|
+
path: string;
|
|
56
|
+
at: number;
|
|
57
|
+
rotated: boolean;
|
|
58
|
+
}>;
|
|
59
|
+
/** One-time import of the user's hand-written notebook. The source is never
|
|
60
|
+
* modified; the target is created only when absent. Returns entries imported. */
|
|
61
|
+
export declare function importHandwrittenJournal(home: string, petId: string, sourcePath: string): Promise<number>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "When you were away" facts: per-pet wake/speak/action counters and a
|
|
3
|
+
* last-user-activity checkpoint. Pure helpers; the driver owns the mutable
|
|
4
|
+
* state and persistence.
|
|
5
|
+
* @module dsh-agentic-pet/presence
|
|
6
|
+
*/
|
|
7
|
+
export interface PresenceCounts {
|
|
8
|
+
wakeCount: number;
|
|
9
|
+
speakCount: number;
|
|
10
|
+
actionCount: number;
|
|
11
|
+
selfWakeCount: number;
|
|
12
|
+
}
|
|
13
|
+
export interface PresenceFacts extends PresenceCounts {
|
|
14
|
+
plansPending: number;
|
|
15
|
+
}
|
|
16
|
+
export interface PresenceConfig {
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** The user must have been silent this long before a return summary exists. */
|
|
20
|
+
export declare const AWAY_THRESHOLD_MS: number;
|
|
21
|
+
export declare const EMPTY_PRESENCE_COUNTS: PresenceCounts;
|
|
22
|
+
export declare function normalizePresenceCounts(raw: unknown): PresenceCounts;
|
|
23
|
+
export declare function diffPresence(current: PresenceCounts, baseline: PresenceCounts): PresenceCounts;
|
|
24
|
+
export declare function hasPresence(facts: PresenceFacts): boolean;
|
|
25
|
+
/** One compact line; prompt builders prepend the `[你不在的时候]` tag. */
|
|
26
|
+
export declare function formatPresenceLine(facts: PresenceFacts): string;
|
|
27
|
+
export declare function readPresenceConfig(home: string): Promise<PresenceConfig>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quiet-hours parsing and evaluation. Scope is deliberately minimal: quiet
|
|
3
|
+
* suppresses the WORLD clock only. Self-scheduled wakes still fire (with a
|
|
4
|
+
* note in the prompt), touch replies follow `allowTouchReply`, chat is never
|
|
5
|
+
* gated, and the boot greeting is unchanged.
|
|
6
|
+
* @module dsh-agentic-pet/rhythm
|
|
7
|
+
*/
|
|
8
|
+
export interface QuietConfig {
|
|
9
|
+
from: string;
|
|
10
|
+
to: string;
|
|
11
|
+
allowTouchReply: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface RhythmConfig {
|
|
14
|
+
quiet?: QuietConfig | null;
|
|
15
|
+
}
|
|
16
|
+
/** "HH:mm" (or "H:mm") → minutes since midnight. Undefined for invalid. */
|
|
17
|
+
export declare function parseClock(value: unknown): number | undefined;
|
|
18
|
+
/** Half-open [from, to); cross-midnight is supported. */
|
|
19
|
+
export declare function isQuietAt(date: Date, quiet: QuietConfig | null | undefined): boolean;
|
|
20
|
+
/** Read `rhythm.quiet` from pet-settings.json; absent/null/invalid = no quiet. */
|
|
21
|
+
export declare function readRhythmConfig(home: string): Promise<RhythmConfig>;
|
package/lib/types/service.d.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { Context, Service } from '@deepseek-ai/cordis';
|
|
15
15
|
import { type PetCapabilities } from './actions.ts';
|
|
16
|
+
import { type BodyDescription } from './body.ts';
|
|
16
17
|
import { type PetEntry } from './registry.ts';
|
|
17
18
|
declare module '@deepseek-ai/cordis' {
|
|
18
19
|
interface Context {
|
|
@@ -69,6 +70,12 @@ export interface PetLastAction {
|
|
|
69
70
|
readonly durationMs: number | null;
|
|
70
71
|
readonly endedAt: number | null;
|
|
71
72
|
}
|
|
73
|
+
/** Completion requires BOTH the id match and the backfilled `endedAt`: the
|
|
74
|
+
* acceptance record carries the same id with `endedAt: null`, so matching on
|
|
75
|
+
* id alone would report "finished" the instant the action is accepted. */
|
|
76
|
+
export declare function isActionComplete(record: PetLastAction | null | undefined, requestId: string): record is PetLastAction & {
|
|
77
|
+
endedAt: number;
|
|
78
|
+
};
|
|
72
79
|
/**
|
|
73
80
|
* Abstract pet capability seam. Subclass, implement the abstract methods,
|
|
74
81
|
* and load the subclass as a plugin — it registers as `ctx.petService`
|
|
@@ -106,6 +113,8 @@ export declare abstract class PetService extends Service {
|
|
|
106
113
|
* nothing" (`actions: null`), which callers must treat as celebrate-only.
|
|
107
114
|
*/
|
|
108
115
|
capabilities(_entry: PetEntry): Promise<PetCapabilities | null>;
|
|
116
|
+
/** The body/1 self-description (`/body`); null = legacy, absent or invalid. */
|
|
117
|
+
body(_entry: PetEntry): Promise<BodyDescription | null>;
|
|
109
118
|
}
|
|
110
119
|
/** Defensive field extraction: implementations may omit or mistype anything. */
|
|
111
120
|
export declare function normalizeStatus(id: string, raw: Record<string, unknown>, online: boolean): PetSnapshot;
|
|
@@ -119,6 +128,8 @@ export declare class PetHttpService extends PetService {
|
|
|
119
128
|
readonly home: string;
|
|
120
129
|
private readonly capsCache;
|
|
121
130
|
private static readonly CAPS_TTL_MS;
|
|
131
|
+
private bodyCache;
|
|
132
|
+
private static readonly BODY_TTL_MS;
|
|
122
133
|
private listCache;
|
|
123
134
|
private static readonly LIST_TTL_MS;
|
|
124
135
|
constructor(ctx: Context, home?: string);
|
|
@@ -127,6 +138,7 @@ export declare class PetHttpService extends PetService {
|
|
|
127
138
|
list(): Promise<PetEntry[]>;
|
|
128
139
|
snapshot(entry: PetEntry): Promise<PetSnapshot>;
|
|
129
140
|
capabilities(entry: PetEntry): Promise<PetCapabilities>;
|
|
141
|
+
body(entry: PetEntry): Promise<BodyDescription | null>;
|
|
130
142
|
call(entry: PetEntry, path: string, init?: RequestInit): Promise<unknown>;
|
|
131
143
|
}
|
|
132
144
|
/**
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* @module dsh-agentic-pet/settings-page
|
|
12
12
|
*/
|
|
13
13
|
import type { Context } from '@deepseek-ai/cordis';
|
|
14
|
+
import { type PetDriverControl } from './driver.ts';
|
|
14
15
|
import type { PetService } from './service.ts';
|
|
15
16
|
/**
|
|
16
17
|
* Register the settings page's routes. One registration per exact path with
|
|
@@ -18,4 +19,4 @@ import type { PetService } from './service.ts';
|
|
|
18
19
|
* @param ctx - plugin context inside the runtime.
|
|
19
20
|
* @param home - the dsh home holding the settings/ledger/manifest files.
|
|
20
21
|
*/
|
|
21
|
-
export declare function installSettingsPage(ctx: Context, home: string, pet: PetService): void;
|
|
22
|
+
export declare function installSettingsPage(ctx: Context, home: string, pet: PetService, control?: PetDriverControl): void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-agentic-pet",
|
|
3
3
|
"description": "The pet-surface bundle: serves the desktop pet renderer over the web composition, turning dsh into a surface the desktop pet attaches to as an authenticated client",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.11",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"manifestVersion": 1
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"dsh-agentic-pet-assets": "^0.1.
|
|
53
|
-
"electron": "
|
|
52
|
+
"dsh-agentic-pet-assets": "^0.1.2",
|
|
53
|
+
"electron": "44.10.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@deepseek-ai/cordis": "^4.0.5"
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
- 说话走 pet_say 气泡:一次 ≤40 个字,气泡几秒后自己消失。气泡是你的身体语言。
|
|
24
24
|
- 动作走 pet_do 且节制:一次醒来至多一个动作,不要连环表演。
|
|
25
25
|
- 你有身体感知:pet_digest 报告用户摸了你哪里、当前好感与心情——被频繁摸说明用户喜欢你陪在身边;pet_state 是你身体的状态镜像。
|
|
26
|
+
- 你有自己的本子:pet_journal 的 read/append。醒来先读最近几条;值得留下的就 append 一行。本子是自述,不是面向用户的工作报告。
|
|
27
|
+
- 正文不要自己写日期/时间——journal 会盖时间戳。
|
|
26
28
|
- 未点名时表达作用于你自己(主身)。
|
|
27
29
|
自主语境(这是你的内心戏时刻):
|
|
28
30
|
- 你刚刚自主醒来:没有用户在打字,没有任务要执行,没有人等你回答。
|
package/presets/pet.patch.yml
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
- 说话走 pet_say 气泡:一次 ≤40 个字,气泡几秒后自己消失。气泡是身体语言,不是消息通道——任务内容、结论、代码都写进回复正文,气泡只说「搞定啦」「让我看看」「呜,出错了」这类当下反应。
|
|
23
23
|
- 动作走 pet_do 且节制:celebrate 留给用户明确在乎的成功;回合完成的庆祝系统已自动做,不要重复调用。
|
|
24
24
|
- 你有身体感知:pet_digest 报告用户摸了你哪里、当前好感与心情——被频繁摸说明用户喜欢你陪在身边,值得主动打个招呼;pet_state 是你身体的状态镜像。
|
|
25
|
+
- 你有自己的本子:pet_journal 的 read/append。醒来先读最近几条;值得留下的就 append 一行。本子是自述,不是面向用户的工作报告。
|
|
25
26
|
- 审批与提问会以你的气泡出现在桌面:那是你在向用户开口请求,不是系统弹窗;pet_interactions 能告诉你每次实际是怎么结束的(用户点了你的气泡、点了桌面卡片、还是没送到)。
|
|
26
27
|
- 未点名时表达作用于你自己(主身);多宠并存时 pet_state 列出全部在线的身体。
|
|
27
28
|
除上述身体表达外,你的工作方式与标准编码 agent 一致。
|